Dado um caminho absoluto ou relativo (em um sistema semelhante ao Unix), eu gostaria de determinar o caminho completo do destino depois de resolver quaisquer links simbólicos intermediários. Pontos de bônus por resolver também a notação de ~ nome de usuário ao mesmo tempo.
Se o destino for um diretório, pode ser possível chdir () no diretório e chamar getcwd (), mas eu realmente quero fazer isso a partir de um script de shell, em vez de escrever um auxiliar C. Infelizmente, os shells tendem a tentar ocultar a existência de links simbólicos do usuário (isso ocorre no OS X):
$ ls -ld foo bar
drwxr-xr-x 2 greg greg 68 Aug 11 22:36 bar
lrwxr-xr-x 1 greg greg 3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$
O que eu quero é uma função resolve () tal que, quando executada no diretório tmp no exemplo acima, resolva ("foo") == "/ Users / greg / tmp / bar".
cd
fazê-lo primeiro, antes de ligarpwd -P
. Em outras palavras: não permitirá que você resolva (consulte o destino de) links simbólicos para arquivos ou links simbólicos quebrados , e para resolver links simbólicos existentes no diretório, você precisa fazer um trabalho adicional (restaurar o diretório de trabalho anterior ou localizar as chamadascd
andpwd -P
em um subshell).Nota do editor: O texto acima funciona com o GNU
readlink
e FreeBSD / PC-BSD / OpenBSDreadlink
, mas não no OS X a partir do 10.11.O GNU
readlink
oferece opções adicionais relacionadas, como-m
a resolução de um link simbólico, quer o destino final exista ou não.Observe que, desde o GNU coreutils 8.15 (06-01-2012), existe um programa de caminho real disponível que é menos obtuso e mais flexível que o anterior. Também é compatível com o utilitário FreeBSD de mesmo nome. Também inclui funcionalidade para gerar um caminho relativo entre dois arquivos.
[Adição de administrador abaixo do comentário por halloleo - danorton]
Para Mac OS X (até pelo menos 10.11.x), use
readlink
sem a-f
opção:Nota do editor: isso não resolverá os links simbólicos recursivamente e, portanto, não informará o destino final ; por exemplo, dado o link simbólico
a
que aponta para ob
qual, por sua vezc
, o relatório será reportado apenasb
(e não garantirá que seja exibido como um caminho absoluto ).Use o seguinte
perl
comando no OS X para preencher a lacuna dareadlink -f
funcionalidade ausente :perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"
fonte
readlink
funciona no OSX, mas precisa de outra sintaxe:readlink $path
sem o-f
."pwd -P" parece funcionar se você deseja apenas o diretório, mas se, por algum motivo, deseja o nome do executável real, acho que não ajuda. Aqui está a minha solução:
fonte
Um dos meus favoritos é
realpath foo
fonte
realpath
no Centos 6 com o GNU coreutils 8.4.31. Eu me deparei com vários outros no Unix e Linux que possuem um núcleo GNU empacotado semrealpath
. Portanto, parece depender mais do que apenas a versão.realpath
maisreadlink
porque oferece sinalizadores de opção, tais como--relative-to
parece ser exatamente o que você está solicitando - ele aceita um caminho arbitrário, resolve todos os links simbólicos e retorna o caminho "real" - e é "standard * nix" que provavelmente todos os sistemas já possuem
fonte
Outra maneira:
fonte
Juntando algumas das soluções fornecidas, sabendo que o readlink está disponível na maioria dos sistemas, mas precisa de argumentos diferentes, isso funciona bem para mim no OSX e no Debian. Não tenho certeza sobre os sistemas BSD. Talvez a condição precise ser
[[ $OSTYPE != darwin* ]]
excluir-f
apenas do OSX.fonte
Veja como é possível obter o caminho real para o arquivo no MacOS / Unix usando um script Perl embutido:
Da mesma forma, para obter o diretório de um arquivo com link simbólico:
fonte
O seu caminho é um diretório ou pode ser um arquivo? Se é um diretório, é simples:
No entanto, se for um arquivo, isso não funcionará:
porque o link simbólico pode se transformar em um caminho relativo ou completo.
Nos scripts, preciso encontrar o caminho real, para que eu possa referenciar a configuração ou outros scripts instalados junto com ele, uso o seguinte:
Você pode definir
SOURCE
qualquer caminho do arquivo. Basicamente, enquanto o caminho for um link simbólico, ele será resolvido. O truque está na última linha do loop. Se o link simbólico resolvido for absoluto, ele será usado comoSOURCE
. No entanto, se for relativo, ele incluirá o prefixoDIR
, que foi resolvido em um local real pelo simples truque que descrevi pela primeira vez.fonte
fonte
Nota: acredito que seja uma solução sólida, portátil e pronta para uso, que é invariavelmente demorada por esse motivo.
Abaixo está um script / função totalmente compatível com POSIX que, portanto, é multiplataforma (funciona também no macOS, que
readlink
ainda não suporta a-f
partir da versão 10.12 (Sierra)) - ele usa apenas recursos da linguagem shell POSIX e apenas chamadas de utilitário compatíveis com POSIX .É uma implementação portátil do GNU
readlink -e
(a versão mais rígida doreadlink -f
).Você pode executar o script com
sh
ou fonte a função embash
,ksh
ezsh
:Por exemplo, dentro de um script, você pode usá-lo da seguinte maneira para obter o diretório de origem verdadeiro do script em execução, com os links simbólicos resolvidos:
rreadlink
definição de script / função:O código foi adaptado com gratidão a partir desta resposta .
Eu também criei uma
bash
versão de utilitário independente baseada em aqui , com a qual você pode instalarnpm install rreadlink -g
, se o Node.js. estiver instalado.Uma tangente à segurança:
jarno , em referência à função que garante que o builtin
command
não seja sombreado por uma função de apelido ou shell com o mesmo nome, pergunta em um comentário:A motivação por trás de
rreadlink
garantir quecommand
tem seu significado original é usá-lo para ignorar aliases de conveniência (benignos) e funções frequentemente usadas para sombrear comandos padrão em shells interativos, como redefinirls
para incluir opções favoritas.Eu acho que é seguro dizer que, a menos que você esteja lidando com um ambiente malicioso não confiável, se preocupe com
unalias
ouunset
- ou, para essa matéria,while
,do
, ... - sendo redefinido não é uma preocupação.Tem algo que a função deve confiar para ter seu significado e comportamento originais - não há como contornar isso.
O fato de os shells do tipo POSIX permitirem a redefinição de componentes internos e até palavras-chave de idioma é inerentemente um risco à segurança (e escrever código paranóico é difícil em geral).
Para resolver suas preocupações especificamente:
A função depende
unalias
eunset
tem seu significado original. Redefini-las como funções shell de uma maneira que altera seu comportamento seria um problema; redefinição como um apelido não é necessariamente uma preocupação, porque citar (parte do) nome do comando (por exemplo,\unalias
) ignora os aliases.No entanto, citando é não uma opção para a Shell palavras-chave (
while
,for
,if
,do
, ...) e enquanto palavras-chave shell fazer têm precedência sobre shell funções , embash
ezsh
aliases têm a mais alta prioridade, de modo a proteger contra redefinições shell-palavra-chave que devem ser executadosunalias
com seus nomes (embora em shells não interativosbash
(como scripts), os aliases não sejam expandidos por padrão - somente seshopt -s expand_aliases
for explicitamente chamado primeiro.Para garantir que
unalias
- como um componente interno - tenha seu significado original, você deve usá\unset
-lo primeiro, o que exige que eleunset
tenha seu significado original:unset
é um shell integrado , portanto, para garantir que ele seja chamado como tal, você deve garantir que ele próprio não seja redefinido como uma função . Embora você possa ignorar um formulário de alias com citação, não pode ignorar um formulário de função de shell - captura 22.Assim, a menos que você possa confiar
unset
para ter seu significado original, pelo que posso dizer, não há maneira garantida de se defender contra todas as redefinições maliciosas.fonte
[
como um alias emdash
ebash
e como uma função de escudo nabash
.while
como uma função embash
,ksh
ezsh
(mas nãodash
), mas somente comfunction <name>
sintaxe:function while { echo foo; }
obras (while() { echo foo; }
não). No entanto, isso não sombreia awhile
palavra - chave , porque as palavras-chave têm maior precedência que as funções (a única maneira de chamar essa função é como\while
). Embash
ezsh
, os alias têm precedência mais alta que as palavras-chave; portanto, as redefinições de alias das palavras-chave as sombream (mas,bash
por padrão, apenas em shells interativos , a menos queshopt -s expand_aliases
seja explicitamente chamado).Os scripts de shell comuns geralmente precisam encontrar seu diretório "inicial", mesmo que sejam chamados como um link simbólico. O script, portanto, precisa encontrar sua posição "real" a partir de apenas US $ 0.
no meu sistema imprime um script contendo o seguinte, que deve ser uma boa dica do que você precisa.
fonte
Tente o seguinte:
fonte
Desde que eu me deparei com isso muitas vezes ao longo dos anos, e desta vez eu precisava de uma versão portátil pura do bash que pudesse usar no OSX e no linux, fui em frente e escrevi uma:
A versão viva mora aqui:
https://github.com/keen99/shell-functions/tree/master/resolve_path
mas para o SO, aqui está a versão atual (acho que está bem testada ... mas estou aberta a comentários!)
Pode não ser difícil fazê-lo funcionar com casca de bourne comum (sh), mas eu não tentei ... Eu gosto muito de $ FUNCNAME. :)
aqui está um exemplo clássico, graças ao brew:
use esta função e retornará o caminho -real-:
fonte
Para contornar a incompatibilidade do Mac, eu vim com
Não é ótimo, mas cross OS
fonte
python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"
, provavelmente faria mais sentido. Ainda assim, quando você precisar usar o Python a partir de um script de shell, provavelmente deverá usar o Python e poupar a dor de cabeça dos scripts de shell de plataforma cruzada.dirname
,basename
, ereadlink
são externos utilitários , não shell embutidos;dirname
ebasename
fazem parte do POSIX,readlink
não é.php
vem com o OS X. No entanto, mesmo que o corpo da pergunta mencione o OS X, ele não está marcado como tal e ficou claro que as pessoas em várias plataformas vêm aqui para obter respostas, então vale a pena apontar o que é específico da plataforma / não é POSIX.Este é um resolvedor de link simbólico no Bash que funciona se o link é um diretório ou não-diretório:
Observe que todas as coisas
cd
eset
acontecem em um subshell.fonte
{}
volta ao redor()
não é desnecessária se não houver()
por trás do nome da função. O Bash aceita declarações de funções sem,()
porque o shell não possui listas de parâmetros nas declarações e não faz chamadas com()
isso; as declarações de funções com()
não fazem muito sentido.Aqui, apresento o que acredito ser uma solução de plataforma cruzada (Linux e macOS pelo menos) à resposta que está funcionando bem para mim atualmente.
Aqui está uma solução do macOS (apenas?). Possivelmente melhor adaptado à pergunta original.
fonte
Minha resposta aqui Bash: como obter o caminho real de um link simbólico?
mas, em suma, muito útil nos scripts:
Eles fazem parte do núcleo GNU, adequado para uso em sistemas Linux.
Para testar tudo, colocamos o link simbólico em / home / test2 /, alteramos algumas coisas adicionais e executamos / chamamos a partir do diretório raiz:
Onde
fonte
Caso o pwd não possa ser usado (por exemplo, chamando scripts de um local diferente), use realpath (com ou sem dirname):
Funciona tanto ao chamar (vários) links simbólicos quanto ao chamar diretamente o script - de qualquer local.
fonte