Importa se existe /foo/barou /foorealmente existe, ou você está interessado apenas no aspecto de manipulação de cadeias de acordo com as regras de nome de caminho?
Chen Levy
5
@twalberg ... que é inventado meio ...
Camilo Martin
2
@CamiloMartin não artificial em tudo - ele faz exatamente o que a pergunta pede - transforma /foo/bar/..a /foo, e usando um bashcomando. Se houver outros requisitos que não sejam reconhecidos, então talvez eles devem ser ...
twalberg
14
@twalberg Você tem feito muito TDD -_- '
Camilo Martin
Respostas:
193
se você deseja remover parte de um nome de arquivo do caminho, "dirname" e "basename" são seus amigos, e "realpath" também é útil.
dirname /foo/bar/baz
# /foo/bar
basename /foo/bar/baz
# baz
dirname $( dirname /foo/bar/baz )# /foo
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp
realpath alternativas
Se realpathnão for suportado pelo seu shell, você pode tentar
readlink -f /path/here/..
Além disso
readlink -m /path/there/../../
Funciona da mesma forma que
realpath -s /path/here/../../
em que o caminho não precisa existir para ser normalizado.
Para aqueles que precisam de uma solução para o OS X, confira a resposta de Adam Liss abaixo.
Trenton
stackoverflow.com/a/17744637/999943 Esta é uma resposta fortemente relacionada! Me deparei com as duas postagens de controle de qualidade em um dia e queria vinculá-las.
Ambos realpathe readlinkvem dos utilitários principais do GNU, então provavelmente você obtém os dois ou nenhum. Se bem me lembro, a versão readlink no Mac é um pouco diferente da versão GNU: - \
Antoine 'hashar' Musso
100
Não sei se existe um comando direto do bash para fazer isso, mas geralmente faço
Isso normalizará, mas não resolverá os links programáveis. Isso pode ser um bug ou um recurso. :-)
Adam Liss
4
Também tem um problema se $ CDPATH estiver definido; porque o "cd foo" mudará para qualquer diretório "foo" que seja um subdiretório de $ CDPATH, não apenas um "foo" que está no diretório atual. Eu acho que você precisa fazer algo como: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P.
MJS
7
A resposta de Tim é definitivamente a mais simples e mais portátil. CDPATH é fácil de lidar: dir = "$ (CDPATH não definido && cd" $ dir "&& pwd)"
David Blevins
2
Isso pode ser muito perigoso ( rm -rf $normalDir) se dirToNormalize não existir!
Frank Meulenaar
4
Sim, provavelmente é melhor usar um &&comentário do @ DavidBlevins.
Elias Dorneles
54
Tente realpath. Abaixo está a fonte na íntegra, doada ao domínio público.
// realpath.c: display the absolute path to a file or directory.// Adam Liss, August, 2007// This program is provided "as-is" to the public domain, without express or// implied warranty, for any non-profit use, provided this notice is maintained.#include<stdio.h>#include<stdlib.h>#include<string.h>#include<libgen.h>#include<limits.h>staticchar*s_pMyName;void usage(void);int main(int argc,char*argv[]){char
sPath[PATH_MAX];
s_pMyName = strdup(basename(argv[0]));if(argc <2)
usage();
printf("%s\n", realpath(argv[1], sPath));return0;}void usage(void){
fprintf(stderr,"usage: %s PATH\n", s_pMyName);
exit(1);}
Você realmente deve encontrar a parte com "Bônus: é um comando bash e disponível em todos os lugares!" parte sobre realpath. Também é o meu favorito, mas em vez de complementar sua ferramenta da fonte. É tudo muito pesado, e na maioria das vezes você só quer readlink -f... BTW, readlinktambém NÃO é um bash embutido, mas faz parte do coreutilsUbuntu.
Tomasz Gandor
4
Você disse que está doando isso para o domínio público, mas o cabeçalho também diz "para qualquer uso sem fins lucrativos" - você realmente pretende doá-lo para o domínio público? Isso significaria que pode ser usado para fins comerciais com fins lucrativos, bem como para organizações sem fins lucrativos ...
me_e
36
Uma solução portátil e confiável é usar python, que é pré-instalado em praticamente todos os lugares (incluindo Darwin). Você tem duas opções:
abspath retorna um caminho absoluto, mas não resolve links simbólicos:
O BSD não possui o sinalizador -f, o que significa que isso falhará mesmo no MacOS Mojave mais recente e em muitos outros sistemas. Esqueça o uso de -f se você quiser portabilidade, muitos sistemas operacionais são afetados.
sorin
1
@sorin. A questão não é sobre Mac, é sobre Linux.
mattalxndr
14
readlinké o padrão do bash para obter o caminho absoluto. Ele também tem a vantagem de retornar cadeias vazias se não houver caminhos ou um caminho (considerando os sinalizadores para isso).
Para obter o caminho absoluto para um diretório que pode ou não existir, mas quem são os pais, use:
abspath=$(readlink -f $path)
Para obter o caminho absoluto para um diretório que deve existir junto com todos os pais:
abspath=$(readlink -e $path)
Para canonizar o caminho fornecido e seguir os links simbólicos, se eles existirem, mas ignorar os diretórios ausentes e retornar o caminho de qualquer maneira, é:
abspath=$(readlink -m $path)
A única desvantagem é que o readlink seguirá os links. Se você não deseja seguir os links, pode usar esta convenção alternativa:
abspath=$(cd ${path%/*}&& echo $PWD/${path##*/})
Isso irá chdir para a parte do diretório de $ path e imprimir o diretório atual junto com a parte do arquivo de $ path. Se não conseguir chdir, você obtém uma string vazia e um erro no stderr.
readlinké uma boa opção se estiver disponível. A versão do OS X não suporta as opções -eou -f. Nos três primeiros exemplos, você deve $pathcolocar aspas duplas para manipular espaços ou curingas no nome do arquivo. +1 para expansão de parâmetros, mas isso tem uma vulnerabilidade de segurança. Se pathestiver vazio, isso será direcionado cdpara o seu diretório pessoal. Você precisa de aspas duplas. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
toxalot
Este foi apenas um exemplo. Se você está interessado em segurança, não deve usar o bash ou qualquer outra variante do shell. Além disso, o bash tem seus próprios problemas quando se trata de compatibilidade entre plataformas, além de ter problemas com a alteração de funcionalidade entre as principais versões. O OSX é apenas uma das muitas plataformas com problemas pertinentes ao script de shell, sem mencionar que é baseado no BSD. Quando você precisa ser verdadeiramente multiplataforma, precisa estar em conformidade com o POSIX, para que a expansão dos parâmetros realmente saia da janela. Dê uma olhada no Solaris ou HP-UX algum tempo.
Craig
6
Não significando nenhuma ofensa aqui, mas apontar questões obscuras como essa é importante. Eu só estou querendo uma resposta rápida para esse problema trivial e eu teria confiado nesse código com qualquer entrada / todas, se não fosse o comentário acima. Também é importante oferecer suporte ao OS-X nessas discussões básicas. Infelizmente, existem muitos comandos que não são suportados no OS-X, e muitos fóruns tomam isso como garantido ao discutir o Bash, o que significa que continuaremos recebendo muitos problemas de plataforma cruzada, a menos que sejam tratados mais cedo ou mais tarde.
Rebs 5/05
13
Pergunta antiga, mas há uma maneira muito mais simples se você estiver lidando com nomes de caminhos completos no nível do shell:
abspath = "$ (cd" $ path "&& pwd)"
Como o CD acontece em um subshell, não afeta o script principal.
Duas variações, supondo que os comandos internos do shell aceitem -L e -P, são:
Pessoalmente, raramente preciso dessa abordagem posterior, a menos que esteja fascinado com links simbólicos por algum motivo.
FYI: variação na obtenção do diretório inicial de um script que funciona mesmo que o script altere seu diretório atual posteriormente.
name0 = "$ (nome da base" $ 0 ")"; #base nome do script
dir0 = "$ (cd" $ (nome do diretório "$ 0") "&& pwd)"; #absolute começando dir
O uso do CD garante que você sempre tenha o diretório absoluto, mesmo que o script seja executado por comandos como ./script.sh que, sem o cd / pwd, geralmente fornecem apenas .. Inútil se o script gravar um CD posteriormente.
Como observou Adam Liss, realpathnão está incluído em todas as distribuições. O que é uma pena, porque é a melhor solução. O código fonte fornecido é ótimo e provavelmente vou começar a usá-lo agora. Aqui está o que eu tenho usado até agora, que eu compartilho aqui apenas para completar:
get_abs_path(){local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd ->/dev/null
echo "$ABS_PATH"}
Se você deseja resolver links simbólicos, substitua pwdpor pwd -P.
Uma dica com a pwd -Popção para este caso ... Considere o que aconteceria se $(basename "$1")fosse um link simbólico para um arquivo em outro diretório. O pwd -Púnico resolve links simbólicos na parte do diretório do caminho, mas não na parte do nome da base.
Eu suspeito que isso falhe se o argumento não for um diretório. Suponha que eu queira saber para onde / usr / bin / java leva?
Edward Falk,
1
Se você souber que é um arquivo, pushd $(dirname /usr/bin/java)tente.
schmunk
5
Não é exatamente uma resposta, mas talvez uma pergunta de acompanhamento (a pergunta original não foi explícita):
readlinké bom se você realmente deseja seguir links simbólicos. Mas há também um caso de uso para apenas normalizando ./e ../e //seqüências, que pode ser feito puramente sintática, sem Canonicalização links simbólicos. readlinknão é bom para isso, e nem é realpath.
for f in $paths;do(cd $f; pwd);done
funciona para caminhos existentes, mas quebra para outros.
Um sedscript parece ser uma boa aposta, exceto que você não pode substituir iterativamente as seqüências ( /foo/bar/baz/../..-> /foo/bar/..-> /foo) sem usar algo como Perl, que não é seguro assumir em todos os sistemas, ou usar um loop feio para comparar a saída desed da sua entrada.
FWIW, uma linha usando Java (JDK 6+):
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
realpathtem uma -sopção para não resolver links simbólicos e apenas referências determinação de /./, /../e remover extras /caracteres. Quando combinado com a -mopção, realpathopera apenas no nome do arquivo e não toca em nenhum arquivo real. Ele soa como a solução perfeita. Mas, infelizmente, realpathainda está faltando em muitos sistemas.
toxalot
A remoção de ..componentes não pode ser feita sintaticamente quando links simbólicos estão envolvidos. /one/two/../threenão é o mesmo /one/threeque twoum link simbólico para /foo/bar.
jrw32982 suporta Monica
@ jrw32982 sim, como eu disse na minha resposta, isso é para um caso de uso em que a canonização do link simbólico não é desejada ou necessária.
Jesse Glick #
@JesseGlick não se trata apenas de você querer ou não canonizar links simbólicos. Seu algoritmo realmente produz a resposta errada. Para que sua resposta seja correta, você precisa saber a priori que não havia links simbólicos envolvidos (ou que eles eram apenas de uma determinada forma). Sua resposta diz que você não deseja canonizá-los, não que não haja links simbólicos envolvidos no caminho.
Jrw32982 suporta Monica
Há casos de uso em que a normalização deve ser executada sem assumir nenhuma estrutura de diretório fixa e existente. A normalização do URI é semelhante. Nesses casos, é uma limitação inerente que o resultado geralmente não esteja correto se houver links simbólicos perto de um diretório em que o resultado será aplicado posteriormente.
perfil completo de Jesse Glick
5
Estou atrasado para a festa, mas esta é a solução que eu criei depois de ler vários tópicos como este:
resolve_dir(){(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"`2>/dev/null;if[ $?-eq 0];then pwd;fi)}
Isso resolverá o caminho absoluto de $ 1, será agradável com ~, manterá os links simbólicos no caminho em que estão e não mexerá com sua pilha de diretórios. Retorna o caminho completo ou nada se não existir. Ele espera que $ 1 seja um diretório e provavelmente falhará, se não for, mas essa é uma verificação fácil de se fazer.
Falador, e resposta um pouco tarde. Preciso escrever um, pois estou preso no RHEL4 / 5 mais antigo. Lida com links absolutos e relativos e simplifica as entradas //, /./ e somedir /../.
Experimente nosso novo produto da biblioteca Bash, realpath-lib, que colocamos no GitHub para uso gratuito e sem ônus. Está completamente documentado e é uma ótima ferramenta de aprendizado.
Ele resolve caminhos locais, relativos e absolutos e não possui nenhuma dependência, exceto o Bash 4+; então deve funcionar em qualquer lugar. É grátis, limpo, simples e instrutivo.
No entanto, isso não resolve os links simbólicos. O caminho real processa os caminhos que começam na raiz e seguem os links simbólicos à medida que progridem. Tudo o que isso faz é recolher as referências pai.
Martijn Pieters
2
Com base na resposta de @ Andre, talvez eu tenha uma versão um pouco melhor, caso alguém esteja atrás de uma solução sem manipulação de loop e completamente baseada em manipulação de string. Também é útil para aqueles que não querem desferir nenhum link simbólico, que é a desvantagem de usar realpathor readlink -f.
Funciona nas versões bash 3.2.25 e superior.
shopt -s extglob
normalise_path(){local path="$1"# get rid of /../ example: /one/../two to /two
path="${path//\/*([!\/])\/\.\./}"# get rid of /./ and //* example: /one/.///two to /one/two
path="${path//@(\/\.\/|\/+(\/))//}"# remove the last '/.'
echo "${path%%/.}"}
$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config
É uma boa idéia, mas perdi 20 minutos tentando fazer com que isso funcionasse em várias versões diferentes do bash. Acontece que a opção extglob shell precisa estar ativada para que isso funcione, e não é por padrão. Quando se trata da funcionalidade do bash, é importante especificar a versão necessária e as opções não padrão, porque esses detalhes podem variar entre os sistemas operacionais. Por exemplo, uma versão recente do Mac OSX (Yosemite) vem apenas com uma versão desatualizada do bash (3.2).
Drwatsoncode
Desculpe @ricovox; Eu já atualizei isso. Estou ansioso para saber a versão exata do Bash que você tem lá. A fórmula acima (atualizada) funciona no CentOS 5.8, que vem com o bash 3.2.25
ϹοδεMεδιϲ
Desculpe pela confusão. Este código funcionou na minha versão do Mac OSX bash (3.2.57) depois de ativar o extglob. Minha observação sobre as versões do bash era mais geral (que na verdade se aplica mais a outra resposta aqui sobre regex no bash).
Drwatsoncode 13/10/2015
2
Agradeço sua resposta. Eu usei como base para mim. Aliás, notei vários casos em que o seu falha: (1) Caminhos relativos hello/../world(2) Pontos no nome do arquivo /hello/..world(3) Pontos após barra dupla /hello//../world(4) Ponto antes ou após barra dupla /hello//./worldou /hello/.//world (5) Pai após corrente: /hello/./../world/ (6) Pai after Parent:, /hello/../../worldetc. - Alguns desses podem ser corrigidos usando um loop para fazer correções até que o caminho pare de mudar. (Também remover dir/../, não /dir/..mas remover dir/..a partir da extremidade.)
drwatsoncode
0
Baseado no excelente snippet python do loveborg, escrevi o seguinte:
#!/bin/sh# Version of readlink that follows links to the end; good for Mac OS Xfor file in"$@";dowhile[-h "$file"];do
l=`readlink $file`case"$l"in/*) file="$l";;*) file=`dirname "$file"`/"$l"esacdone#echo $file
python -c "import os,sys; print os.path.abspath(sys.argv[1])""$file"done
Aqui está um breve caso de teste com alguns espaços distorcidos nos caminhos para exercitar completamente a citação
mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello">"/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt ""/tmp/test/ second path / green .txt "
cd "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "
Eu sei que esta é uma pergunta antiga. Eu ainda estou oferecendo uma alternativa. Recentemente, encontrei o mesmo problema e não encontrei nenhum comando portátil e existente para fazer isso. Então, eu escrevi o seguinte script de shell, que inclui uma função que pode fazer o truque.
#! /bin/sh function normalize {local rc=0local ret
if[ $# -gt 0 ] ; then# invalidif["x`echo $1 | grep -E '^/\.\.'`"!="x"];then
echo $1
return-1fi# convert to absolute pathif["x`echo $1 | grep -E '^\/'`"=="x"];then
normalize "`pwd`/$1"return $?fi
ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`else
read line
normalize "$line"return $?fiif["x`echo $ret | grep -E '/\.\.?(/|$)'`"!="x"];then
ret=`normalize "$ret"`
rc=$?fi
echo "$ret"return $rc
}
Eu criei uma função exclusiva para lidar com isso, com foco no desempenho mais alto possível (por diversão). Ele não resolve os links simbólicos, portanto é basicamente o mesmo que realpath -sm.
## A bash-only mimic of `realpath -sm`. ## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath (){
${*+false}&&{>&2 echo $FUNCNAME: missing operand;return1;};local c s p IFS='/';## path chunk, absolute path, input path, IFS for splitting paths into chunkslocal-i r=0;## return valuefor p in"$@";docase"$p"in## Check for leading backslashes, identify relative/absolute path'')((r|=1));continue;;//[!/]*)>&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem";((r|=2));continue;;/*);;*) p="$PWD/$p";;## Prepend the current directory to form an absolute pathesac
s='';for c in $p;do## Let IFS split the path at '/'scase $c in### NOTE: IFS is '/'; so no quotes needed here''|.);;## Skip duplicate '/'s and '/./'s..) s="${s%/*}";;## Trim the previous addition to the absolute path string*) s+=/$c;;### NOTE: No quotes here intentionally. They make no difference, it seemsesac;done;
echo "${s:-/}";## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` insteaddonereturn $r;}
Nota: Esta função não trata de caminhos iniciados com //, pois exatamente duas barras duplas no início de um caminho são um comportamento definido pela implementação. No entanto, ele lida com /,/// e assim por diante muito bem.
Essa função parece lidar com todos os casos extremos corretamente, mas ainda pode haver alguns por aí que eu não lidei.
Nota de desempenho: quando chamado com milhares de argumentos, abspathroda cerca de 10x mais lentamente que realpath -sm; quando chamado com um único argumento, abspathexecuta> 110x mais rápido do que realpath -smna minha máquina, principalmente devido à não necessidade de executar um novo programa todas as vezes.
a stat -f %N ~/Documentslinha é um arenque vermelho ... seu shell está substituindo ~/Documentspor /Users/me/Documentse statestá imprimindo seu argumento literalmente.
/foo/bar
ou/foo
realmente existe, ou você está interessado apenas no aspecto de manipulação de cadeias de acordo com as regras de nome de caminho?/foo/bar/..
a/foo
, e usando umbash
comando. Se houver outros requisitos que não sejam reconhecidos, então talvez eles devem ser ...Respostas:
se você deseja remover parte de um nome de arquivo do caminho, "dirname" e "basename" são seus amigos, e "realpath" também é útil.
realpath
alternativasSe
realpath
não for suportado pelo seu shell, você pode tentarAlém disso
Funciona da mesma forma que
em que o caminho não precisa existir para ser normalizado.
fonte
realpath
ereadlink
vem dos utilitários principais do GNU, então provavelmente você obtém os dois ou nenhum. Se bem me lembro, a versão readlink no Mac é um pouco diferente da versão GNU: - \Não sei se existe um comando direto do bash para fazer isso, mas geralmente faço
e funciona bem.
fonte
rm -rf $normalDir
) se dirToNormalize não existir!&&
comentário do @ DavidBlevins.Tente
realpath
. Abaixo está a fonte na íntegra, doada ao domínio público.fonte
realpath
. Também é o meu favorito, mas em vez de complementar sua ferramenta da fonte. É tudo muito pesado, e na maioria das vezes você só querreadlink -f
... BTW,readlink
também NÃO é um bash embutido, mas faz parte docoreutils
Ubuntu.Uma solução portátil e confiável é usar python, que é pré-instalado em praticamente todos os lugares (incluindo Darwin). Você tem duas opções:
abspath
retorna um caminho absoluto, mas não resolve links simbólicos:python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
realpath
retorna um caminho absoluto e, ao fazer isso, resolve links simbólicos, gerando um caminho canônico:python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
Em cada caso,
path/to/file
pode ser um caminho relativo ou absoluto.fonte
readlink
está disponível no OS X, mas não com a-f
opção. Soluções portáteis discutidas aqui .Use o utilitário readlink do pacote coreutils.
fonte
readlink
é o padrão do bash para obter o caminho absoluto. Ele também tem a vantagem de retornar cadeias vazias se não houver caminhos ou um caminho (considerando os sinalizadores para isso).Para obter o caminho absoluto para um diretório que pode ou não existir, mas quem são os pais, use:
Para obter o caminho absoluto para um diretório que deve existir junto com todos os pais:
Para canonizar o caminho fornecido e seguir os links simbólicos, se eles existirem, mas ignorar os diretórios ausentes e retornar o caminho de qualquer maneira, é:
A única desvantagem é que o readlink seguirá os links. Se você não deseja seguir os links, pode usar esta convenção alternativa:
Isso irá chdir para a parte do diretório de $ path e imprimir o diretório atual junto com a parte do arquivo de $ path. Se não conseguir chdir, você obtém uma string vazia e um erro no stderr.
fonte
readlink
é uma boa opção se estiver disponível. A versão do OS X não suporta as opções-e
ou-f
. Nos três primeiros exemplos, você deve$path
colocar aspas duplas para manipular espaços ou curingas no nome do arquivo. +1 para expansão de parâmetros, mas isso tem uma vulnerabilidade de segurança. Sepath
estiver vazio, isso será direcionadocd
para o seu diretório pessoal. Você precisa de aspas duplas.abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
Pergunta antiga, mas há uma maneira muito mais simples se você estiver lidando com nomes de caminhos completos no nível do shell:
Como o CD acontece em um subshell, não afeta o script principal.
Duas variações, supondo que os comandos internos do shell aceitem -L e -P, são:
Pessoalmente, raramente preciso dessa abordagem posterior, a menos que esteja fascinado com links simbólicos por algum motivo.
FYI: variação na obtenção do diretório inicial de um script que funciona mesmo que o script altere seu diretório atual posteriormente.
O uso do CD garante que você sempre tenha o diretório absoluto, mesmo que o script seja executado por comandos como ./script.sh que, sem o cd / pwd, geralmente fornecem apenas .. Inútil se o script gravar um CD posteriormente.
fonte
Como observou Adam Liss,
realpath
não está incluído em todas as distribuições. O que é uma pena, porque é a melhor solução. O código fonte fornecido é ótimo e provavelmente vou começar a usá-lo agora. Aqui está o que eu tenho usado até agora, que eu compartilho aqui apenas para completar:Se você deseja resolver links simbólicos, substitua
pwd
porpwd -P
.fonte
pwd -P
opção para este caso ... Considere o que aconteceria se$(basename "$1")
fosse um link simbólico para um arquivo em outro diretório. Opwd -P
único resolve links simbólicos na parte do diretório do caminho, mas não na parte do nome da base.Minha solução recente foi:
Com base na resposta de Tim Whitcomb.
fonte
pushd $(dirname /usr/bin/java)
tente.Não é exatamente uma resposta, mas talvez uma pergunta de acompanhamento (a pergunta original não foi explícita):
readlink
é bom se você realmente deseja seguir links simbólicos. Mas há também um caso de uso para apenas normalizando./
e../
e//
seqüências, que pode ser feito puramente sintática, sem Canonicalização links simbólicos.readlink
não é bom para isso, e nem érealpath
.funciona para caminhos existentes, mas quebra para outros.
Um
sed
script parece ser uma boa aposta, exceto que você não pode substituir iterativamente as seqüências (/foo/bar/baz/../..
->/foo/bar/..
->/foo
) sem usar algo como Perl, que não é seguro assumir em todos os sistemas, ou usar um loop feio para comparar a saída desed
da sua entrada.FWIW, uma linha usando Java (JDK 6+):
fonte
realpath
tem uma-s
opção para não resolver links simbólicos e apenas referências determinação de/./
,/../
e remover extras/
caracteres. Quando combinado com a-m
opção,realpath
opera apenas no nome do arquivo e não toca em nenhum arquivo real. Ele soa como a solução perfeita. Mas, infelizmente,realpath
ainda está faltando em muitos sistemas...
componentes não pode ser feita sintaticamente quando links simbólicos estão envolvidos./one/two/../three
não é o mesmo/one/three
quetwo
um link simbólico para/foo/bar
.Estou atrasado para a festa, mas esta é a solução que eu criei depois de ler vários tópicos como este:
Isso resolverá o caminho absoluto de $ 1, será agradável com ~, manterá os links simbólicos no caminho em que estão e não mexerá com sua pilha de diretórios. Retorna o caminho completo ou nada se não existir. Ele espera que $ 1 seja um diretório e provavelmente falhará, se não for, mas essa é uma verificação fácil de se fazer.
fonte
Falador, e resposta um pouco tarde. Preciso escrever um, pois estou preso no RHEL4 / 5 mais antigo. Lida com links absolutos e relativos e simplifica as entradas //, /./ e somedir /../.
fonte
Experimente nosso novo produto da biblioteca Bash, realpath-lib, que colocamos no GitHub para uso gratuito e sem ônus. Está completamente documentado e é uma ótima ferramenta de aprendizado.
Ele resolve caminhos locais, relativos e absolutos e não possui nenhuma dependência, exceto o Bash 4+; então deve funcionar em qualquer lugar. É grátis, limpo, simples e instrutivo.
Você pode fazer:
Esta função é o núcleo da biblioteca:
Ele também contém funções para get_dirname, get_filename, get_ stemname e validate_path. Experimente em várias plataformas e ajude a melhorá-lo.
fonte
O problema
realpath
é que ele não está disponível no BSD (ou no OSX). Aqui está uma receita simples extraída de um artigo bastante antigo (2009) do Linux Journal , que é bastante portátil:Observe que essa variante também não requer que o caminho exista.
fonte
Com base na resposta de @ Andre, talvez eu tenha uma versão um pouco melhor, caso alguém esteja atrás de uma solução sem manipulação de loop e completamente baseada em manipulação de string. Também é útil para aqueles que não querem desferir nenhum link simbólico, que é a desvantagem de usar
realpath
orreadlink -f
.Funciona nas versões bash 3.2.25 e superior.
fonte
hello/../world
(2) Pontos no nome do arquivo/hello/..world
(3) Pontos após barra dupla/hello//../world
(4) Ponto antes ou após barra dupla/hello//./world
ou/hello/.//world
(5) Pai após corrente:/hello/./../world/
(6) Pai after Parent:,/hello/../../world
etc. - Alguns desses podem ser corrigidos usando um loop para fazer correções até que o caminho pare de mudar. (Também removerdir/../
, não/dir/..
mas removerdir/..
a partir da extremidade.)Baseado no excelente snippet python do loveborg, escrevi o seguinte:
fonte
Isso funciona mesmo se o arquivo não existir. Exige que o diretório que contém o arquivo exista.
fonte
realpath -e
Eu precisava de uma solução que fizesse todos os três:
realpath
ereadlink -f
são addonsNenhuma das respostas tinha os números 1 e 2. Adicionei o número 3 para salvar outras pessoas.
Aqui está um breve caso de teste com alguns espaços distorcidos nos caminhos para exercitar completamente a citação
fonte
Eu sei que esta é uma pergunta antiga. Eu ainda estou oferecendo uma alternativa. Recentemente, encontrei o mesmo problema e não encontrei nenhum comando portátil e existente para fazer isso. Então, eu escrevi o seguinte script de shell, que inclui uma função que pode fazer o truque.
https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c
fonte
Eu criei uma função exclusiva para lidar com isso, com foco no desempenho mais alto possível (por diversão). Ele não resolve os links simbólicos, portanto é basicamente o mesmo que
realpath -sm
.Nota: Esta função não trata de caminhos iniciados com
//
, pois exatamente duas barras duplas no início de um caminho são um comportamento definido pela implementação. No entanto, ele lida com/
,///
e assim por diante muito bem.Essa função parece lidar com todos os casos extremos corretamente, mas ainda pode haver alguns por aí que eu não lidei.
Nota de desempenho: quando chamado com milhares de argumentos,
abspath
roda cerca de 10x mais lentamente querealpath -sm
; quando chamado com um único argumento,abspath
executa> 110x mais rápido do querealpath -sm
na minha máquina, principalmente devido à não necessidade de executar um novo programa todas as vezes.fonte
Descobri hoje que você pode usar o
stat
comando para resolver caminhos.Portanto, para um diretório como "~ / Documents":
Você pode executar isso:
stat -f %N ~/Documents
Para obter o caminho completo:
/Users/me/Documents
Para links simbólicos, você pode usar a opção de formato% Y:
stat -f %Y example_symlink
O que pode retornar um resultado como:
/usr/local/sbin/example_symlink
As opções de formatação podem ser diferentes em outras versões do * NIX, mas funcionaram para mim no OSX.
fonte
stat -f %N ~/Documents
linha é um arenque vermelho ... seu shell está substituindo~/Documents
por/Users/me/Documents
estat
está imprimindo seu argumento literalmente.Uma solução simples usando
node.js
:fonte