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 cd
especificações de
Se a -P
opção estiver em vigor, a $PWD
variá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 $PWD
variá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 pwd
utilitá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 -P
deve ser impresso e que cd -
deve ser impresso $OLDPWD
conforme 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
. cd
define $PWD
assim que cd -P .
$PWD
agora 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 $PWD
para $HOME
em 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 $0
o caminho, mas, no caso em $0
si, é 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 ls
limpo, compatível com POSIX _function()
. Ele ainda funcionará bem sem o último, embora possa sobrescrever unset
algumas 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 $PWD
para 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 stderr
por 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 $_zdlm
variá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 $OLDPWD
não possa, de forma alguma, ser contado quando terminar.
Ele tenta encerrar cada um de seus argumentos o quanto antes. Ele primeiro tenta cd
entrar $1
. Se puder, imprime o caminho canônico do argumento para stdout
. Se não puder, verifica se $1
existe 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 $1
seja um link simbólico que não aponte para um diretório. Nesse caso, ele chama while
loop em um subshell.
Chama ls
para 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 ls
saída o mínimo necessário para conter completamente o nome do link e a string ->
. Embora eu tenha tentado evitar isso shift
e, ao que $IFS
parece, 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 ls
definitivamente não seja um link vinculado. Nesse ponto, canoniza esse caminho como antes e cd
depois 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' -> ...