Existe um comando para recuperar o caminho absoluto, dado o caminho relativo?
Por exemplo, quero que $ line contenha o caminho absoluto de cada arquivo em dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Existe um comando para recuperar o caminho absoluto, dado o caminho relativo?
Por exemplo, quero que $ line contenha o caminho absoluto de cada arquivo em dir ./etc/
find ./ -type f | while read line; do
echo $line
done
Respostas:
usar:
find "$(pwd)"/ -type f
para obter todos os arquivos ou
echo "$(pwd)/$line"
para exibir o caminho completo (se o caminho relativo for importante)
$(pwd)
no lugar de $PWD
? (sim, eu sei que pwd
é um builtin)
Tente realpath
.
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
Para evitar a expansão de links simbólicos, use realpath -s
.
A resposta vem do " comando bash / fish para imprimir o caminho absoluto para um arquivo ".
realpath
parece não estar disponível no Mac (OS X 10.11 "El Capitan"). :-(
realpath
também não parece estar disponível no CentOS 6
brew install coreutils
trarárealpath
realpath
já está presente. Não precisei instalá-lo separadamente.
realpath
está disponível no Git for Windows (pelo menos para mim).
Se você tem o pacote coreutils instalado, geralmente pode usá-lo readlink -f relative_file_name
para recuperar o absoluto (com todos os links simbólicos resolvidos)
brew install coreutils
. No entanto, o executável é prefixado por ag:greadlink -f relative_file_name
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPD Algumas explicações
"$1"
dirname "$1"
cd "$(dirname "$1")
nesse diretório relativo e obtemos o caminho absoluto para ele executando o pwd
comando shell$(basename "$1")
echo
elerealpath
ou readlink -f
fazer. Por exemplo, ele não funciona em caminhos nos quais o último componente é um link simbólico.
-P
opção de pwd
comando:echo "$(cd "$(dirname "$1")"; pwd -P)/$(basename "$1")"
Pelo que vale, votei na resposta que foi escolhida, mas queria compartilhar uma solução. A desvantagem é que é apenas o Linux - passei cerca de 5 minutos tentando encontrar o equivalente OSX antes de chegar ao Stack overflow. Tenho certeza de que está lá fora.
No Linux, você pode usar readlink -e
em conjunto com dirname
.
$(dirname $(readlink -e ../../../../etc/passwd))
rendimentos
/etc/
E então você usa dirname
a irmã de, basename
para obter o nome do arquivo
$(basename ../../../../../passwd)
rendimentos
passwd
Coloque tudo junto ..
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
rendimentos
/etc/passwd
Você está seguro se estiver direcionando um diretório, basename
não retornará nada e você acabará com duas barras na saída final.
dirname
, readlink
e basename
. Isso me ajudou a obter o caminho absoluto de um link simbólico - não o seu alvo.
/home/GKFX
digitando touch newfile
, antes de pressionar enter, você poderá descobrir que eu quero dizer "create / home / GKFX / newfile", que é um caminho absoluto para um arquivo que ainda não existe.
Eu acho que este é o mais portátil:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
Falhará se o caminho ainda não existir.
dirname
é um utilitário principal do GNU, não é comum a todos os unixen que acredito.
dirname
é um utilitário padrão do POSIX, consulte aqui: pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html
${1##*/}
há um dia e agora que substituí o lixo por basename "$1"
ele parece finalmente estar manipulando adequadamente os caminhos que terminam em /.
../..
realpath
provavelmente é melhor
Mas ...
A questão inicial era muito confusa, para começar, com um exemplo pouco relacionado à questão, conforme indicado.
A resposta selecionada, na verdade, responde ao exemplo dado, e não à pergunta do título. O primeiro comando é essa resposta (é mesmo? Duvido), e poderia funcionar tão bem sem o '/'. E não vejo o que o segundo comando está fazendo.
Vários problemas são mistos:
transformando um nome de caminho relativo em um absoluto, o que quer que ele denuncie, possivelmente nada. ( Normalmente, se você emitir um comando como touch foo/bar
, o nome do caminho foo/bar
deve existir para você e, possivelmente, ser usado na computação, antes que o arquivo seja realmente criado. )
pode haver vários nomes de caminho absolutos que denotam o mesmo arquivo (ou arquivo potencial), principalmente por causa de links simbólicos (links simbólicos) no caminho, mas possivelmente por outros motivos (um dispositivo pode ser montado duas vezes como somente leitura). Pode-se ou não querer resolver explicitamente esses links simbólicos.
chegando ao final de uma cadeia de links simbólicos para um arquivo ou nome sem vínculo simbólico. Isso pode ou não gerar um nome de caminho absoluto, dependendo de como é feito. E pode-se, ou não, querer resolvê-lo em um nome de caminho absoluto.
O comando readlink foo
sem opção fornece uma resposta apenas se o argumento foo
for um link simbólico, e essa resposta for o valor desse link simbólico. Nenhum outro link é seguido. A resposta pode ser um caminho relativo: qualquer que seja o valor do argumento do link simbólico.
Contudo, readlink
possui opções (-f -e ou -m) que funcionarão para todos os arquivos e fornecerão um nome de caminho absoluto (aquele sem links simbólicos) para o arquivo realmente indicado pelo argumento.
Isso funciona bem para qualquer coisa que não seja um link simbólico, embora se queira usar um nome de caminho absoluto sem resolver os links simbólicos intermediários no caminho. Isso é feito pelo comandorealpath -s foo
No caso de um argumento de link simbólico, readlink
com suas opções novamente resolverá todos os links simbólicos no caminho absoluto para o argumento, mas isso também incluirá todos os links simbólicos que podem ser encontrados seguindo o valor do argumento. Você pode não querer isso se desejar um caminho absoluto para o próprio link simbólico do argumento, e não para o que quer que ele possa vincular. Novamente, se foo
for um link simbólico, realpath -s foo
obterá um caminho absoluto sem resolver links simbólicos, incluindo o fornecido como argumento.
Sem a -s
opção, realpath
faz praticamente o mesmo que
readlink
, exceto pela simples leitura do valor de um link, além de várias outras coisas. Simplesmente não está claro para mim por que readlink
tem suas opções, criando aparentemente uma redundância indesejável com
realpath
.
Explorar a web não diz muito mais, exceto que pode haver algumas variações entre os sistemas.
Conclusão: realpath
é o melhor comando para usar, com a maior flexibilidade, pelo menos para o uso solicitado aqui.
Minha solução favorita foi a única do @EugenKonkov, porque não implicava a presença de outros utilitários (o pacote coreutils).
Mas falhou nos caminhos relativos "." e "..", então aqui está uma versão ligeiramente aprimorada que trata desses casos especiais.
Ele ainda falha se o usuário não tiver permissão para cd
entrar no diretório pai do caminho relativo.
#! /bin/sh
# Takes a path argument and returns it as an absolute path.
# No-op if the path is already absolute.
function to-abs-path {
local target="$1"
if [ "$target" == "." ]; then
echo "$(pwd)"
elif [ "$target" == ".." ]; then
echo "$(dirname "$(pwd)")"
else
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
fi
}
A resposta de Eugen não funcionou muito bem para mim, mas isso fez:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
Nota lateral, seu diretório de trabalho atual não é afetado.
A melhor solução, imho, é a postada aqui: https://stackoverflow.com/a/3373298/9724628 .
Requer python para funcionar, mas parece cobrir todos ou quase todos os casos extremos e é uma solução muito portátil.
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
Se você estiver usando bash no Mac OS X, que não tem realpath existia nem a sua readlink pode imprimir o caminho absoluto, você pode ter escolha a não ser código sua própria versão para imprimi-lo. Aqui está a minha implementação:
(festança pura)
abspath(){
local thePath
if [[ ! "$1" =~ ^/ ]];then
thePath="$PWD/$1"
else
thePath="$1"
fi
echo "$thePath"|(
IFS=/
read -a parr
declare -a outp
for i in "${parr[@]}";do
case "$i" in
''|.) continue ;;
..)
len=${#outp[@]}
if ((len==0));then
continue
else
unset outp[$((len-1))]
fi
;;
*)
len=${#outp[@]}
outp[$len]="$i"
;;
esac
done
echo /"${outp[*]}"
)
}
(use gawk)
abspath_gawk() {
if [[ -n "$1" ]];then
echo $1|gawk '{
if(substr($0,1,1) != "/"){
path = ENVIRON["PWD"]"/"$0
} else path = $0
split(path, a, "/")
n = asorti(a, b,"@ind_num_asc")
for(i in a){
if(a[i]=="" || a[i]=="."){
delete a[i]
}
}
n = asorti(a, b, "@ind_num_asc")
m = 0
while(m!=n){
m = n
for(i=1;i<=n;i++){
if(a[b[i]]==".."){
if(b[i-1] in a){
delete a[b[i-1]]
delete a[b[i]]
n = asorti(a, b, "@ind_num_asc")
break
} else exit 1
}
}
}
n = asorti(a, b, "@ind_num_asc")
if(n==0){
printf "/"
} else {
for(i=1;i<=n;i++){
printf "/"a[b[i]]
}
}
}'
fi
}
(puro bsd awk)
#!/usr/bin/env awk -f
function abspath(path, i,j,n,a,b,back,out){
if(substr(path,1,1) != "/"){
path = ENVIRON["PWD"]"/"path
}
split(path, a, "/")
n = length(a)
for(i=1;i<=n;i++){
if(a[i]==""||a[i]=="."){
continue
}
a[++j]=a[i]
}
for(i=j+1;i<=n;i++){
delete a[i]
}
j=0
for(i=length(a);i>=1;i--){
if(back==0){
if(a[i]==".."){
back++
continue
} else {
b[++j]=a[i]
}
} else {
if(a[i]==".."){
back++
continue
} else {
back--
continue
}
}
}
if(length(b)==0){
return "/"
} else {
for(i=length(b);i>=1;i--){
out=out"/"b[i]
}
return out
}
}
BEGIN{
if(ARGC>1){
for(k=1;k<ARGC;k++){
print abspath(ARGV[k])
}
exit
}
}
{
print abspath($0)
}
exemplo:
$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
Se o caminho relativo for um caminho de diretório, tente o meu, deve ser o melhor:
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
Uma melhoria na versão bastante agradável do @ernest-a:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
Isso lida corretamente com o caso em que está o último elemento do caminho ..
; nesse caso, a "$(pwd)/$(basename "$1")"
resposta do inernest-a será apresentada como accurate_sub_path/spurious_subdirectory/..
.
Se você deseja transformar uma variável que contém um caminho relativo em absoluto, isso funciona:
dir=`cd "$dir"`
"cd" ecoa sem alterar o diretório de trabalho, porque executado aqui em um sub-shell.
dir=`cd ".."` && echo $dir
Essa é uma solução encadeada de todas as outras, por exemplo, quando realpath
falha, ou porque não está instalada ou porque sai com o código de erro; então, a próxima solução é tentada até encontrar o caminho certo.
#!/bin/bash
function getabsolutepath() {
local target;
local changedir;
local basedir;
local firstattempt;
target="${1}";
if [ "$target" == "." ];
then
printf "%s" "$(pwd)";
elif [ "$target" == ".." ];
then
printf "%s" "$(dirname "$(pwd)")";
else
changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;
# If everything fails... TRHOW PYTHON ON IT!!!
local fullpath;
local pythoninterpreter;
local pythonexecutables;
local pythonlocations;
pythoninterpreter="python";
declare -a pythonlocations=("/usr/bin" "/bin");
declare -a pythonexecutables=("python" "python2" "python3");
for path in "${pythonlocations[@]}";
do
for executable in "${pythonexecutables[@]}";
do
fullpath="${path}/${executable}";
if [[ -f "${fullpath}" ]];
then
# printf "Found ${fullpath}\\n";
pythoninterpreter="${fullpath}";
break;
fi;
done;
if [[ "${pythoninterpreter}" != "python" ]];
then
# printf "Breaking... ${pythoninterpreter}\\n"
break;
fi;
done;
firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
# printf "Error: Could not determine the absolute path!\\n";
return 1;
fi
}
printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"
Você pode usar a substituição do string bash para qualquer caminho relativo $ line:
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
A substituição básica da frente de cadeia segue a fórmula
$ {string / # substring / Replacement}
que é discutida aqui: https://www.tldp.org/LDP/abs/html/string-manipulation.html
O \
caractere nega /
quando queremos que ele faça parte da string que encontramos / substituímos.
Não consegui encontrar uma solução que fosse perfeitamente portátil entre o Mac OS Catalina, o Ubuntu 16 e o Centos 7, por isso decidi fazê-lo com python inline e funcionou bem para meus scripts bash.
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"