Convertendo caminho relativo em caminho absoluto sem link simbólico

63

Existe um comando Unix para obter o caminho absoluto (e canônico) de um caminho relativo que pode conter links simbólicos?

Benjamin
fonte

Respostas:

80

Você pode usar o readlinkutilitário, com a -fopção:

-f, --canonicalize

      canonicalize  by  following  every symlink in every component of
      the given name recursively; all  but  the  last  component  must
      exist

Algumas distribuições, por exemplo, as que usam o GNU coreutils e o FreeBSD , também vêm com um realpath(1)utilitário que basicamente apenas chama realpath(3)e faz praticamente a mesma coisa.

Esteira
fonte
17
A -fopção não existe no Mac OS X (pelo menos a partir de 10.8.5). Sem a -fopção, apenas retorna um código de erro.
Iconoclast
4
Alguns sistemas não vêm com readlinknem realpath- Solaris e HP-UX, em particular.
Sildoreth
Para o problema do Mac: brew install coreutils apple.stackexchange.com/a/69332/23040 , e se você não desejar executar a etapa que redireciona todas as ferramentas padrão da CLI do Mac para os equivalentes GNU recém-instalados, basta ligar greadlink.
Adrian
19

Portably, a PWDvariável é definida pelo shell para um local absoluto do diretório atual. Qualquer componente desse caminho pode ser um link simbólico.

case $f in
  /*) absolute=$f;;
  *) absolute=$PWD/$f;;
esac

Se você deseja eliminar .e ..também, mude para o diretório que contém o arquivo e obtenha $PWDlá:

if [ -d "$f" ]; then f=$f/.; fi
absolute=$(cd "$(dirname -- "$f")"; printf %s. "$PWD")
absolute=${absolute%?}
absolute=$absolute/${f##*/}

Não existe uma maneira portátil de seguir links simbólicos. Se você possui um caminho para um diretório, na maioria das unidades, ele $(cd -- "$dir" && pwd -P 2>/dev/null || pwd)fornece um caminho que não usa links simbólicos, porque shells que rastreiam links simbólicos tendem a ser implementados pwd -P("P" para "físico").

Alguns departamentos fornecem um utilitário para imprimir o caminho "físico" para um arquivo.

  • Sistemas Linux razoavelmente recentes (com GNU coreutils ou BusyBox) possuem readlink -f, assim como FreeBSD ≥8.3, NetBSD ≥4.0 e OpenBSD já em 2.2.
  • O FreeBSD ≥4.3 possui realpath(também está presente em alguns sistemas Linux e no BusyBox).
  • Se o Perl estiver disponível, você poderá usar o Cwdmódulo .

    perl -MCwd -e 'print Cwd::realpath($ARGV[0])' path/to/file
Gilles 'SO- parar de ser mau'
fonte
Por que você introduz algo em pwd( $(cd -- "$dir" && pwd -P 2>/dev/null | pwd))? Parece que não se preocupa com stdin.
Mateusz Piotrowski
1
@MateuszPiotrowski Typo, eu pretendia escrever||
Gilles 'SO- stop be evil' '
5

É pwdadequado às suas necessidades? Fornece o caminho absoluto do diretório atual. Ou talvez o que você queira seja realpath () .

xanpeng
fonte
3

alistere rss67no presente artigo introduzir mais estável, compatível e maneira mais fácil. Eu nunca vi uma maneira melhor do que isso antes.

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`

Se você quiser voltar ao local original,

ORGPATH=`pwd`
RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd $ORGPATH

ou

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd -

Eu desejo que isso ajude. Essa foi a melhor solução para mim.

Eonil
fonte
12
Na verdade, esse não é um bom caminho. Mudar de um diretório e voltar a entrar não é confiável: talvez você não tenha permissão para voltar ou o diretório pode ter sido renomeado nesse meio tempo. Em vez disso, altere os diretórios em um subnível: ABSPATH=$(cd -- "$RELPATH" && pwd). Observe as aspas duplas em torno da substituição (para não quebrar se o caminho contiver espaços em branco e caracteres brilhantes) e o --(caso o caminho comece com -). Além disso, isso funciona apenas para diretórios e não canoniza links simbólicos conforme solicitado. Veja minha resposta para mais detalhes.
Gilles 'SO- stop be evil'
"> você pode não ter permissão para voltar" Você quer dizer cd para um diretório, mas não pode voltar? Você poderia fornecer alguns exemplos de cenários.
Talespin_Kit 12/04
0

As outras respostas aqui foram boas, mas insuficientes para minhas necessidades. Eu precisava de uma solução que pudesse usar nos meus scripts em qualquer máquina. Minha solução foi escrever um script de shell que eu possa chamar dos scripts em que eu preciso.

#!/bin/sh
if [ $# -eq 0 ] || [ $# -gt 2 ]; then
  printf 'Usage: respath path [working-directory]\n' >&2
  exit 1
fi
cantGoUp=

path=$1
if [ $# -gt 1 ]; then
  cd "$2"
fi
cwd=`pwd -P` #Use -P option to account for directories that are actually symlinks

#Handle non-relative paths, which don't need resolution
if echo "$path" | grep '^/' > /dev/null ; then
  printf '%s\n' "$path"
  exit 0
fi

#Resolve for each occurrence of ".." at the beginning of the given path.
#For performance, don't worry about ".." in the middle of the path.
while true
do
  case "$path" in
    ..*)
      if [ "$cwd" = '/' ]; then
        printf 'Invalid relative path\n' >&2
        exit 1
      fi
      if [ "$path" = '..' ]; then
        path=
      else
        path=`echo "$path" | sed 's;^\.\./;;'`
      fi
      cwd=`dirname $cwd`
      ;;
    *)
      break
      ;;
  esac
done

cwd=`echo "$cwd" | sed 's;/$;;'`
if [ -z "$path" ]; then
  if [ -z "$cwd" ]; then
    cwd='/'
  fi
  printf '%s\n' "$cwd"
else
  printf '%s/%s\n' "$cwd" "$path"
fi

Esta solução foi escrita para o Bourne Shell para portabilidade. Eu tenho isso em um script chamado "respath.sh".

Este script pode ser usado assim:

respath.sh '/path/to/file'

Ou assim

respath.sh '/relative/path/here' '/path/to/resolve/relative/to'

O script resolve o caminho no primeiro argumento usando o caminho do segundo argumento como ponto de partida. Se apenas o primeiro argumento for fornecido, o script resolverá o caminho relativo ao seu diretório de trabalho atual.

Sildoreth
fonte
Observe que você provavelmente encontrará apenas um shell Bourne no Solaris 10 e versões anteriores hoje em dia. Esse shell Bourne, como a maioria das implementações de shell Bourne desde o SVR2 (1984), foi pwdincorporado e não suporta -P(o shell Bourne não implementou esse manuseio lógico de $ PWD como os shells POSIX).
Stéphane Chazelas
Se você abandonar a compatibilidade com Bourne, poderá usar a sintaxe POSIX $ {var ## pattern} e evitar os ecos que serão quebrados com alguns valores.
Stéphane Chazelas
Você esqueceu de verificar o status de saída do cd (e da senha). Você provavelmente deve usar cd -P --bem no caso do 1º argumento contém alguns ..
Stéphane Chazelas
Seu casecheque deve ser em ..|../*)vez de ..*.
Stéphane Chazelas
Há um caso de citações ausentesdirname $cwd
Stéphane Chazelas 22/15/15
0

Caso, além disso, você tenha algum caminho predefinido para $1(geralmente ${PWD}), o seguinte funcionaria:

CWD="${1:-${PredefinedAbsolutePath}}"
if [ "${CWD}" != "${PredefinedAbsolutePath}}" ]; then
    case $1 in
        /*) echo "CWD=${CWD}"; ;;
        *) CWD=$(realpath $1); echo "CWD=${CWD}"; ;;
    esac
fi
Nikhil
fonte
0

Respeitando a resposta de todos, achei a função abaixo muito mais fácil e portátil entre o Linux / MAC OSX do que outras que encontrei em todos os lugares. Pode ajudar alguém.

#!/usr/bin/bash
get_absolute_path(){
    file_path=`pwd $1`
    echo "$file_path/$1"
}
#to assign to a variable
absolute_path=$(get_absolute_path "file.txt")
echo $absolute_path
lauksas
fonte
-1

Suponha que a variável a armazene um nome de caminho (a = "tanto faz")

1. Para o caminho potencial que contém espaço:

c=$(grep -o " " <<< $a | wc -l); if (( $c > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi

2. Para a pergunta real, ou seja, a conversão do caminho para absoluto: o readlink pode recuperar o conteúdo do link simbólico, que pode ser empregado da seguinte maneira. (a observação readlink -fseria perfeita, mas não está disponível no mínimo no Mac OS X bash, no qual -f significa FORMATS .)

if [[ $(readlink $a) == "" ]]; then a=$(cd $a; pwd); else a=$(cd $(readlink $a); pwd); fi

3. Apenas aprendi pwd -Pem man pwd: -P é " Exibir o diretório de trabalho físico atual (todos os links simbólicos resolvidos) " e, portanto,

if (( $(grep -o " " <<< $a | wc -l) > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi; a=$(cd $a; pwd -P)

deve funcionar também.

Conclusão: combine 1 com 2 para atender a ambos os requisitos, enquanto os nomes de caminhos que contêm espaço já são tratados nos 3 mais concisos .

vxtal
fonte
Você está errado. Por exemplo, se o seu caminho incluir um espaço, a maioria dos itens acima não funcionará.
Anthon
ou um char glob às vezes. E isso não resolve os links simbólicos, conforme solicitado.
precisa saber é o seguinte
Excluí minha resposta de "comentário" em relação aos nomes de caminhos que contêm espaços e modifiquei a parte "resposta" de acordo.
vxtal