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
Foi útil?

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:

  1. Isso também tem o efeito colateral de resolver todos os links simbólicos. Isso pode ou não ser desejável, mas geralmente é.
  2. readlink dará um resultado em branco se você referir um diretório inexistente. Se você deseja suportar caminhos inexistentes, use readlink -m em vez de. Infelizmente, essa opção não existe nas versões do ReadLink lançado antes de ~ 2005.

Usando

# 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 que dirname "$relative_file"
  • ${relative_file##*/} é o mesmo resultado que basename "$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

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 que getcwd(). Links simbólicos e componentes de caminho relativo (. e ..) estão resolvidos de devolver o nome do caminho canônico, assim como realpath.

    use Cwd 'abs_path';
    my $abs_path = abs_path($file);
    
  • realpath() é um sinônimo de abs_path()

    use Cwd 'realpath';
    my $abs_path = realpath($file);
    
  • fast_abs_path() é uma versão mais perigosa, mas potencialmente mais rápida de abs_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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top