Como recuperar o caminho absoluto de um arquivo arbitrário do OS X

18

Estou procurando por um comando simples que possa ser usado no Bash para encontrar o caminho absoluto e canônico para um arquivo em um OS X (semelhante a `` readlink -f '' no Linux).

A seguinte sessão de amostra do bash descreve um utilitário [fictício] chamado `` abspath '' que exibe o comportamento desejado:

$ pwd
/Users/guyfleegman

$ ls -lR
drwxr-xr-x  4 guyfleegman  crew  136 Oct 30 02:09 foo

./foo:
-rw-r--r--  1 guyfleegman  crew  0 Oct 30 02:07 bar.txt
lrwxr-xr-x  1 guyfleegman  crew  7 Oct 30 02:09 baz.txt -> bar.txt


$ abspath .
/Users/guyfleegman

$ abspath foo
/Users/guyfleegman/foo

$ abspath ./foo/bar.txt
/Users/guyfleegman/foo/bar.txt

$ abspath foo/baz.txt
/Users/guyfleegman/foo/baz.txt

Como na última chamada de `` abspath '' no exemplo acima, eu preferiria que não resolvesse links simbólicos automaticamente, mas não serei muito exigente aqui.

Michael Wehner
fonte

Respostas:

16
function abspath() { pushd . > /dev/null; if [ -d "$1" ]; then cd "$1"; dirs -l +0; else cd "`dirname \"$1\"`"; cur_dir=`dirs -l +0`; if [ "$cur_dir" == "/" ]; then echo "$cur_dir`basename \"$1\"`"; else echo "$cur_dir/`basename \"$1\"`"; fi; fi; popd > /dev/null; }

Exemplos:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store
mschrag
fonte
11

Eu não acho que exista um comando buildin que faça isso. Jesse Wilson escreveu um script bash para isso:

#!/bin/bash
cd -P -- "$(dirname -- "$1")" &&
printf '%s\n' "$(pwd -P)/$(basename -- "$1")"

No entanto, ele não funciona bem para caminhos diretamente abaixo /, como /etc(impressão //etc), bem como .e ..(impressão /cwd/.nos dois casos). Tentei modificá-lo, mas meu bash-fu insuficiente falhou comigo.

Aqui está a minha sugestão:

#!/usr/bin/env python
import os.path
import sys

for arg in sys.argv[1:]:
    print os.path.abspath(arg)

Salve como /usr/bin/abspathou algo parecido e torne-o executável. Saída de amostra:

Servus08:~ danielbeck$ abspath .
/Users/danielbeck
Servus08:~ danielbeck$ abspath /tmp
/tmp
Servus08:~ danielbeck$ abspath Documents
/Users/danielbeck/Documents
Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/tmp
/Users/danielbeck/Documents

Se você fazer deseja symlink resolução, altere a printlinha como esta:

    print os.path.realpath(os.path.abspath(arg))

para conseguir esta:

Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/private/tmp
/Users/danielbeck/Documents
Daniel Beck
fonte
11
Eu acho que você está no caminho certo com a primeira abordagem baseada em shell, mas acredito que o uso de outra linguagem para fazer isso derruba um pouco o objetivo, pois introduz dependências adicionais e é praticamente equivalente a simplesmente compilar a versão GNU do `readlink (1) 'no OS X (supondo que isso possa ser feito; ainda não o verifiquei).
Michael Wehner
2
@ Michael Você está em um sistema com o GNU readlink ou no OS X - certo? O OS X tem o Python pronto para uso. Em teoria, é uma nova dependência, na prática não. Enfim, é tudo o que posso lhe oferecer.
Daniel Beck
A abordagem pragmática que você está sugerindo é louvável, se for excessivamente simplista. De qualquer forma, se adotássemos essa abordagem em vez de qualquer coisa escrita puramente no bash (o que é absolutamente factível e não depende de mais nada - simplesmente não pode ser feito facilmente em uma linha), o Python provavelmente não será é a melhor escolha. O Perl e o Ruby (e provavelmente o PHP) podem fazer isso de forma sucinta na linha de comando, sem a necessidade de criar um arquivo de script real.
Michael Wehner
11
@ Michael: Verdade, mas não era isso que eu estava comentando. Ofereço a você uma solução de 90%, escrita em puro bash por Jesse Wilson + a análise por que é apenas 90%. Se isso não é problema para você, tudo bem. Também dei uma solução 100% um pouco mais complicada em Python. Embora outras linguagens de script possam ser mais breves (Perl é tão infame :-)), todas exigem um arquivo adicional para armazenar o comando. Ou você deseja escrevê-lo toda vez que quiser usá-lo? É também por isso que adicionei a capacidade de várias linhas para lidar com vários parâmetros, 1 ou 2 linhas, para não fazer diferença.
Daniel Beck
2
@ Michael, por que o Python não seria uma boa escolha no OS X? Ele ainda vem com comandos que são realmente scripts Python, comofile /usr/bin/xattr
Arjan
8

Uma opção seria apenas instalar o coreutils e usar greadlink -f. Ele resolve links simbólicos e funciona com /Foo/ou ~/foo.txtse eles não existem, mas não com /Foo/foo.txtse /Foo/não existe.

$ brew install coreutils
$ greadlink -f /etc
/private/etc
$ greadlink -f ~/Documents/
/Users/lauri/Documents
$ greadlink -f ..
/Users
$ greadlink -f //etc/..////
/private
$ greadlink -f /Foo
/Foo
$ greadlink -f /Foo/foo.txt
$ 

Isso não resolve os links simbólicos e também não funciona /Foo/foo.txt.

abspath() {
  if [ -d "$1" ]; then
    ( cd "$1"; dirs -l +0 )
  else
    ( cd "$(dirname "$1")"; d=$(dirs -l +0); echo "${d%/}/${1##*/}" )
  fi
}

abspath /etc # /etc
abspath ~/Foo/foo.txt # doesn't work
abspath ~/Foo # works
abspath .
abspath ./
abspath ../
abspath ..
abspath /
abspath ~
abspath ~/
abspath ~/Documents
abspath /\"\ \'
abspath /etc/../etc/
abspath /private//etc/
abspath /private//
abspath //private # //private
abspath ./aa.txt
abspath aa.tar.gz
abspath .aa.txt
abspath /.DS_Store
abspath ~/Documents/Books/

dirs -lrealiza expansão til. dirs +0imprime apenas o diretório superior se houver outros diretórios na pilha.

Lri
fonte
2

Eu acho que você poderia fazê-lo com python ou ruby.

$ ruby -e 'puts File.expand_path("~/somepath")'

ou faça um comando com

#!/usr/bin/env ruby
puts File.expand_path(ARGV[0])
Jay
fonte
Meu comentário anterior sobre a resposta de Daniel Beck também se aplica aqui (eu preferiria uma solução puramente Bash-y), embora, se eu recorrer ao uso de outro idioma para conseguir isso, eu gosto mais da sua solução até agora por sua brevidade. :) Eu provavelmente também encerraria a chamada para ruby ​​(por exemplo, a função abspath () {ruby -e "coloca File.expand_path ('$ 1')";}) e a coloco no meu `.profile '.
Michael Wehner
1

Se você possui o módulo File :: Spec instalado para perl, basta fazer o seguinte:

perl -MFile::Spec -e 'print File::Spec->rel2abs("../however/complex/../you/want/to.get"), "\n"'
Trapaceiro
fonte
0

Para scripts bash / sh, você pode usar esta função recursiva:

canonical_readlink ()
{
    cd `dirname $1`;
    __filename=`basename $1`;
    if [ -h "$__filename" ]; then
        canonical_readlink `readlink $__filename`;
    else
        echo "`pwd -P`";
    fi
}

answer=$(canonical_readlink $0)
Gregory Burd
fonte
Algumas variáveis ​​e substituições de comandos não são citadas corretamente. Você pode usar variáveis ​​locais em vez de nomes de variáveis ​​como __filename. Atualmente, o script se comporta de dirnamemaneira semelhante .
Lri
0

Instale a seguinte biblioteca para OSX:

brew install coreutils

greadlink -f file.txt
anh_ng8
fonte
0

Se a instalação do coreutils não for uma opção, o seguinte manipula combinações de links simbólicos,. e .. e funciona em arquivos e pastas como o GNU realpath:

#!/usr/bin/env bash
realpath()
{
    if ! pushd $1 &> /dev/null; then 
        pushd ${1##*/} &> /dev/null
        echo $( pwd -P )/${1%/*}
    else
        pwd -P
    fi
    popd > /dev/null
}

Mas não suporta realpath - relativo a. Isso exigiria /programming//a/12498485/869951 .

Oliver
fonte
0

Dado que a restrição é o MacOS (OS X na época), o PHP está disponível por padrão. Isso retornará o diretório raiz do arquivo. Remova dirnamepara obter o arquivo também.

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
danemacmillan
fonte