Desde que você tenha permissões de execução no diretório atual - ou no diretório a partir do qual você executou o shell script - se desejar um caminho absoluto para um diretório, tudo o que você precisa é cd.
Etapa 10 das cdespecificações de
Se a -Popção estiver em vigor, a $PWDvariável de ambiente deve ser configurada para a string que seria emitida por pwd -P. Se houver permissão insuficiente no novo diretório, ou em qualquer pai desse diretório, para determinar o diretório de trabalho atual, o valor da $PWDvariável de ambiente não será especificado.
E em pwd -P
O nome do caminho gravado na saída padrão não deve conter nenhum componente que se refira aos arquivos do tipo link simbólico. Se houver vários nomes de caminho que o pwdutilitário possa gravar na saída padrão, um começando com um caractere único / barra e um ou mais começando com dois caracteres / barra, ele deverá escrever o nome do caminho começando com um caractere único / barra. O nome do caminho não deve conter caracteres desnecessários / barra após os caracteres um ou dois iniciais / barra.
É porque cd -Pé necessário definir o diretório de trabalho atual como o que pwd -Pdeve ser impresso e que cd -deve ser impresso $OLDPWDconforme o seguinte:
mkdir ./dir
ln -s ./dir ./ln
cd ./ln ; cd . ; cd -
SAÍDA
/home/mikeserv/test/ln
espere por isso...
cd -P . ; cd . ; cd -
SAÍDA
/home/mikeserv/test/dir
E quando eu imprimo com cd -estou imprimindo $OLDPWD. cddefine $PWDassim que cd -P . $PWDagora for um caminho absoluto para /- então não preciso de outras variáveis. E, na verdade, eu nem deveria precisar da trilha, .mas há um comportamento especificado de redefinir $PWDpara $HOMEem um shell interativo quando não cdé adornado. Portanto, é apenas um bom hábito para se desenvolver.
Portanto, basta fazer o acima no caminho ${0%/*}deve ser mais do que suficiente para verificar $0o caminho, mas, no caso em $0si, é um link direto, você provavelmente não poderá alterar o diretório nele, infelizmente.
Aqui está uma função que irá lidar com isso:
zpath() { cd -P . || return
_out() { printf "%s$_zdlm\n" "$PWD/${1##*/}"; }
_cd() { cd -P "$1" ; } >/dev/null 2>&1
while [ $# -gt 0 ] && _cd .
do if _cd "$1"
then _out
elif ! [ -L "$1" ] && [ -e "$1" ]
then _cd "${1%/*}"; _out "$1"
elif [ -L "$1" ]
then ( while set -- "${1%?/}"; _cd "${1%/*}"; [ -L "${1##*/}" ]
do set " $1" "$(_cd -; ls -nd -- "$1"; echo /)"
set -- "${2#*"$1" -> }"
done; _out "$1"
); else ( PS4=ERR:\ NO_SUCH_PATH; set -x; : "$1" )
fi; _cd -; shift; done
unset -f _out _cd; unset -v _zdlm
}
Ele se esforça para fazer o máximo que pode no shell atual - sem chamar um subshell - embora existam subshells invocados para erros e links flexíveis que não apontam para diretórios. Depende de um shell compatível com POSIX e de um espaço para nome lslimpo, compatível com POSIX _function(). Ele ainda funcionará bem sem o último, embora possa sobrescrever unsetalgumas funções atuais do shell nesse caso. Em geral, todas essas dependências devem estar disponíveis de maneira confiável em uma máquina Unix.
Chamada com ou sem argumentos, a primeira coisa que faz é redefinir $PWDpara seu valor canônico - resolve todos os links para seus alvos, conforme necessário. Chamado sem argumentos e é isso; mas ligou para eles e resolverá e canonizará o caminho para cada um ou então imprimirá uma mensagem dizendo stderrpor que não.
Como ele opera principalmente no shell atual, deve ser capaz de lidar com uma lista de argumentos de qualquer tamanho. Ele também procura a $_zdlmvariável (que também unseté quando\n termina ) e imprime seu valor escapado de C imediatamente à direita de cada um de seus argumentos, sendo que cada um deles é sempre seguido também por um único caractere de linha de linha.
Ele altera bastante o diretório, mas, além de defini-lo com seu valor canônico, ele não afeta $PWD, embora $OLDPWDnão possa, de forma alguma, ser contado quando terminar.
Ele tenta encerrar cada um de seus argumentos o quanto antes. Ele primeiro tenta cdentrar $1. Se puder, imprime o caminho canônico do argumento para stdout. Se não puder, verifica se $1existe e não é um link flexível. Se verdadeiro, ele imprime.
Dessa maneira, ele lida com qualquer argumento de tipo de arquivo que o shell tenha permissões para endereçar, a menos que $1seja um link simbólico que não aponte para um diretório. Nesse caso, ele chama whileloop em um subshell.
Chama lspara ler o link. O diretório atual deve ser alterado para seu valor inicial primeiro, a fim de manipular com segurança quaisquer caminhos de referência e, portanto, no subshell de substituição de comando, a função:
cd -...ls...echo /
Ele tira da esquerda da lssaída o mínimo necessário para conter completamente o nome do link e a string ->. Embora eu tenha tentado evitar isso shifte, ao que $IFSparece, esse é o método mais confiável o mais próximo possível. É a mesma coisa que o poor_mans_readlink de Gilles faz - e é bem feito.
Ele repetirá esse processo em um loop até que o nome do arquivo retornado lsdefinitivamente não seja um link vinculado. Nesse ponto, canoniza esse caminho como antes e cddepois imprime.
Exemplo de uso:
zpath \
/tmp/script \ #symlink to $HOME/test/dir/script.sh
ln \ #symlink to ./dir/
ln/nl \ #symlink to ../..
/dev/fd/0 \ #currently a here-document like : dash <<\HD
/dev/fd/1 \ #(zlink) | dash
file \ #regular file
doesntexist \ #doesnt exist
/dev/disk/by-path/pci-0000:00:16.2-usb-0:3:1.0-scsi-0:0:0:0 \
/dev/./././././././null \
. ..
SAÍDA
/home/mikeserv/test/dir/script.sh
/home/mikeserv/test/dir/
/home/mikeserv/test/
/tmp/zshtpKRVx (deleted)
/proc/17420/fd/pipe:[1782312]
/home/mikeserv/test/file
ERR: NO_SUCH_PATH: doesntexist
/dev/sdd
/dev/null
/home/mikeserv/test/
/home/mikeserv/
Ou possivelmente ...
ls
dir/ file file? folder/ link@ ln@ script* script3@ script4@
zdlm=\\0 zpath * | cat -A
SAÍDA
/home/mikeserv/test/dir/^@$
/home/mikeserv/test/file^@$
/home/mikeserv/test/file$
^@$
/home/mikeserv/test/folder/^@$
/home/mikeserv/test/file$ #'link' -> 'file\n'
^@$
/home/mikeserv/test/dir/^@$ #'ln' -> './dir'
/home/mikeserv/test/script^@$
/home/mikeserv/test/dir/script.sh^@$ #'script3' -> './dir/script.sh'
/home/mikeserv/test/dir/script.sh^@$ #'script4' -> '/tmp/script' -> ...