Converter caminho relativo em caminho absoluto?
-
27-09-2019 - |
Pergunta
Não tenho certeza se esses caminhos são duplicados. Dado o caminho relativo, como determino o caminho absoluto usando um script de shell?
Exemplo:
relative path: /x/y/../../a/b/z/../c/d
absolute path: /a/b/c/d
Solução
A partir de esta fonte vem:
#!/bin/bash
# Assume parameter passed in is a relative path to a directory.
# For brevity, we won't do argument type or length checking.
ABS_PATH=`cd "$1"; pwd` # double quotes for paths that contain spaces etc...
echo "Absolute path: $ABS_PATH"
Você também pode fazer uma linha perl, por exemplo, usando Cwd::abs_path
Outras dicas
O método mais confiável que encontrei no Unix é readlink -f
:
$ readlink -f /x/y/../../a/b/z/../c/d
/a/b/c/d
Duas advertências:
- Isso também tem o efeito colateral de resolver todos os links simbólicos. Isso pode ou não ser desejável, mas geralmente é.
readlink
dará um resultado em branco se você referir um diretório inexistente. Se você deseja suportar caminhos inexistentes, usereadlink -m
em vez de. Infelizmente, essa opção não existe nas versões do ReadLink lançado antes de ~ 2005.
Usando Bash
# Directory
relative_dir="folder/subfolder/"
absolute_dir="$( cd "$relative_dir" && pwd )"
# File
relative_file="folder/subfolder/file"
absolute_file="$( cd "${relative_file%/*}" && pwd )"/"${relative_file##*/}"
${relative_file%/*}
é o mesmo resultado quedirname "$relative_file"
${relative_file##*/}
é o mesmo resultado quebasename "$relative_file"
Ressalvas: Não resolve links simbólicos (ou seja, não canoniza o caminho) => pode não diferenciar todas as duplicatas se você usar links simbólicos.
Usando realpath
Comando realpath
faz o trabalho. Uma alternativa é usar readlink -e
(ou readlink -f
). No entanto realpath
geralmente não é instalado por padrão. Se você não pode ter certeza realpath
ou readlink
está presente, você pode substituí -lo usando Perl (veja abaixo).
Usando perl
Steven Kramer propõe um alias de shell se realpath
não está disponível em seu sistema:
$ alias realpath="perl -MCwd -e 'print Cwd::realpath(\$ARGV[0]),qq<\n>'"
$ realpath path/folder/file
/home/user/absolute/path/folder/file
ou se você preferir usar diretamente Perl:
$ perl -MCwd -e 'print Cwd::realpath($ARGV[0]),qq<\n>' path/folder/file
/home/user/absolute/path/folder/file
Este comando perl de uma linha usa Cwd::realpath
. De fato, existem três funções Perl. Eles pegam um único argumento e retornam o nome do caminho absoluto. Abaixo, os detalhes são da documentação Perl5> Módulos principais> CWD.
abs_path()
usa o mesmo algoritmo quegetcwd()
. Links simbólicos e componentes de caminho relativo (.
e..
) estão resolvidos de devolver o nome do caminho canônico, assim comorealpath
.use Cwd 'abs_path'; my $abs_path = abs_path($file);
realpath()
é um sinônimo deabs_path()
use Cwd 'realpath'; my $abs_path = realpath($file);
fast_abs_path()
é uma versão mais perigosa, mas potencialmente mais rápida deabs_path()
use Cwd 'fast_abs_path'; my $abs_path = fast_abs_path($file);
Essas funções são exportadas apenas mediante solicitação =>, portanto, use Cwd
Para evitar o "Sub -rotina indefinida" erro conforme apontado por Arielf. Se você quiser importar todas essas três funções, você pode usar um único use Cwd
linha:
use Cwd qw(abs_path realpath fast_abs_path);
Dê uma olhada em 'Realpath'.
$ realpath
usage: realpath [-q] path [...]
$ realpath ../../../../../
/data/home
Desde que encontrei isso muitas vezes ao longo dos anos, e desta vez eu precisava de uma versão portátil Pure Bash que eu pudesse usar no OSX e no Linux, fui em frente e escrevi um:
A versão viva vive aqui:
https://github.com/keen99/shell-functions/tree/master/resolve_path
Mas, por causa disso, aqui está a versão atual (sinto que está bem testado ... mas estou aberto a feedback!)
Pode não ser difícil fazê -lo funcionar para a Plain Bourne Shell (SH), mas eu não tentei ... gosto muito de $ funcname. :)
#!/bin/bash
resolve_path() {
#I'm bash only, please!
# usage: resolve_path <a file or directory>
# follows symlinks and relative paths, returns a full real path
#
local owd="$PWD"
#echo "$FUNCNAME for $1" >&2
local opath="$1"
local npath=""
local obase=$(basename "$opath")
local odir=$(dirname "$opath")
if [[ -L "$opath" ]]
then
#it's a link.
#file or directory, we want to cd into it's dir
cd $odir
#then extract where the link points.
npath=$(readlink "$obase")
#have to -L BEFORE we -f, because -f includes -L :(
if [[ -L $npath ]]
then
#the link points to another symlink, so go follow that.
resolve_path "$npath"
#and finish out early, we're done.
return $?
#done
elif [[ -f $npath ]]
#the link points to a file.
then
#get the dir for the new file
nbase=$(basename $npath)
npath=$(dirname $npath)
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -d $npath ]]
then
#the link points to a directory.
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
echo "opath [[ $opath ]]" >&2
echo "npath [[ $npath ]]" >&2
return 1
fi
else
if ! [[ -e "$opath" ]]
then
echo "$FUNCNAME: $opath: No such file or directory" >&2
return 1
#and break early
elif [[ -d "$opath" ]]
then
cd "$opath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -f "$opath" ]]
then
cd $odir
ndir=$(pwd -P)
nbase=$(basename "$opath")
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
echo "opath [[ $opath ]]" >&2
return 1
fi
fi
#now assemble our output
echo -n "$ndir"
if [[ "x${nbase:=}" != "x" ]]
then
echo "/$nbase"
else
echo
fi
#now return to where we were
cd "$owd"
return $retval
}
Aqui está um exemplo clássico, graças a Brew:
%% ls -l `which mvn`
lrwxr-xr-x 1 draistrick 502 29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn
Use esta função e ele retornará o caminho -real:
%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`
%% test.sh
relative symlinked path:
/usr/local/bin/mvn
and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn
Pode ser que isso ajude:
$path = "~user/dir/../file"
$resolvedPath = glob($path); # (To resolve paths with '~')
# Since glob does not resolve relative path, we use abs_path
$absPath = abs_path($path);