Comando bash / fish para imprimir o caminho absoluto para um arquivo

Pergunta: existe um comando simples sh / bash / zsh / fish / … para imprimir o caminho absoluto de qualquer arquivo que eu o alimente?

Caso de uso: Estou no diretório /a/b e gostaria de imprimir o caminho completo para o arquivo c na linha de comando, para que eu possa colá-lo facilmente em outro programa: /a/b/c . Simples, mas um pequeno programa para fazer isso provavelmente poderia me salvar 5 segundos ou mais quando se trata de lidar com caminhos longos, o que no final sum. Então me surpreende que não consiga encontrar um utilitário padrão para fazer isso – realmente não existe nenhum?

Aqui está uma implementação de amostra, abspath.py:

 #!/usr/bin/python # Author: Diggory Hardy  # Licence: public domain # Purpose: print the absolute path of all input paths import sys import os.path if len(sys.argv)>1: for i in range(1,len(sys.argv)): print os.path.abspath( sys.argv[i] ) sys.exit(0) else: print >> sys.stderr, "Usage: ",sys.argv[0]," PATH." sys.exit(1) 

Tente realpath .

 ~ $ realpath example.txt /home/username/example.txt 

Tente o readlink que resolverá os links simbólicos:

 readlink -e /foo/bar/baz 
 #! /bin/sh echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")" 
 $ readlink -m FILE /path/to/FILE 

Isso é melhor que readlink -e FILE ou realpath , porque funciona mesmo se o arquivo não existir.

Esqueça o readlink e o realpath que podem ou não estar instalados no seu sistema.

Expandindo a resposta do dogbane acima, aqui é expresso como uma function:

 #!/bin/bash get_abs_filename() { # $1 : relative filename echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" } 

você pode usá-lo assim:

 myabsfile=$(get_abs_filename "../../foo/bar/file.txt") 

Como e por que isso funciona?

A solução explora o fato de que o comando pwd do Bash imprimirá o caminho absoluto do diretório atual quando invocado sem argumentos.

Por que eu gosto dessa solução?

É portável e não requer nenhum readlink ou realpath que freqüentemente não existe em uma instalação padrão de uma dada distribuição Linux / Unix.

E se dir não existir?

Como indicado acima, a function falhará e imprimirá no stderr se o caminho do diretório fornecido não existir. Isso pode não ser o que você quer. Você pode expandir a function para lidar com essa situação:

 #!/bin/bash get_abs_filename() { # $1 : relative filename if [ -d "$(dirname "$1")" ]; then echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" fi } 

Agora, ele retornará uma string vazia se um dos diretórios pai não existir.

Como você lida com trailing ‘..’ ou ‘.’ na input?

Bem, ele dá um caminho absoluto nesse caso, mas não um mínimo. Será parecido com:

 /Users/bob/Documents/.. 

Se você quiser resolver o ‘..’ você precisará fazer o script como:

 get_abs_filename() { # $1 : relative filename filename=$1 parentdir=$(dirname "${filename}") if [ -d "${filename}" ]; then echo "$(cd "${filename}" && pwd)" elif [ -d "${parentdir}" ]; then echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")" fi } 

Esse caminho relativo para a function de shell do conversor de caminho absoluto

  • não requer utilitários (apenas cd e pwd )
  • funciona para diretórios e arquivos
  • alças .. e .
  • manipula espaços em dir ou nomes de arquivos
  • requer que o arquivo ou diretório exista
  • não retorna nada se nada existir no caminho dado
  • lida com caminhos absolutos como input (passa-os essencialmente)

Código:

 function abspath() { # generate absolute path from relative path # $1 : relative filename # return : absolute path if [ -d "$1" ]; then # dir (cd "$1"; pwd) elif [ -f "$1" ]; then # file if [[ $1 = /* ]]; then echo "$1" elif [[ $1 == */* ]]; then echo "$(cd "${1%/*}"; pwd)/${1##*/}" else echo "$(pwd)/$1" fi fi } 

Amostra:

 # assume inside /parent/cur abspath file.txt => /parent/cur/file.txt abspath . => /parent/cur abspath .. => /parent abspath ../dir/file.txt => /parent/dir/file.txt abspath ../dir/../dir => /parent/dir # anything cd can handle abspath doesnotexist => # empty result if file/dir does not exist abspath /file.txt => /file.txt # handle absolute path input 

Nota: Isso é baseado nas respostas de nolan6000 e bsingh , mas corrige o caso do arquivo.

Eu também entendo que a pergunta original era sobre um utilitário de linha de comando existente. Mas como essa parece ser a pergunta no stackoverflow para isso incluindo scripts de shell que querem ter dependencies mínimas, eu coloquei essa solução de script aqui, para que eu possa encontrá-la mais tarde 🙂

O comando find pode ajudar

 find $PWD -name ex* find $PWD -name example.log 

Lista todos os arquivos dentro ou abaixo do diretório atual com nomes correspondentes ao padrão. Você pode simplificá-lo se você obter apenas alguns resultados (por exemplo, diretório próximo da parte inferior da tree contendo poucos arquivos), apenas

 find $PWD 

Eu uso isso no Solaris 10, que não tem os outros utilitários mencionados.

Se você não tem utilitários de readlink ou realpath, você pode usar a seguinte function que funciona em bash e zsh (não tenho certeza sobre o resto).

 abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; } 

Isso também funciona para arquivos inexistentes (assim como a function python os.path.abspath ).

Infelizmente abspath ./../somefile não se livra dos pontos.

Aqui está uma function somente de zsh que eu gosto por sua compacidade. Ele usa o modificador de expansão ‘A’ – veja zshexpn (1).

 realpath() { for f in "$@"; do echo ${f}(:A); done } 

Geralmente, não existe o absolute path para um arquivo (essa afirmação significa que pode haver mais de um em geral, portanto, o uso do artigo definido não é apropriado). Um absolute path é qualquer caminho que inicie a partir da raiz “/” e designe um arquivo sem ambigüidade independentemente do diretório de trabalho (consulte, por exemplo, wikipedia ).

Um relative path é um caminho que deve ser interpretado a partir de outro diretório. Pode ser o diretório de trabalho se for um relative path sendo manipulado por um aplicativo (embora não necessariamente). Quando está em um link simbólico em um diretório, geralmente ele é relativo a esse diretório (embora o usuário possa ter outros usos em mente).

Portanto, um caminho absoluto é apenas um caminho relativo ao diretório raiz.

Um caminho (absoluto ou relativo) pode ou não conter links simbólicos. Caso contrário, também é um tanto impermeável a mudanças na estrutura de vinculação, mas isso não é necessariamente necessário ou mesmo desejável. Algumas pessoas chamam canonical path (ou canonical file name ou resolved path ) um absolute path no qual todos os links simbólicos foram resolvidos, isto é, foram substituídos por um caminho para o qual eles se conectam. Os comandos realpath e readlink buscam um caminho canônico, mas somente o realpath tem uma opção para obter um caminho absoluto sem se preocupar em resolver links simbólicos (junto com várias outras opções para obter vários tipos de caminhos, absolutos ou relativos a algum diretório).

Isso requer várias observações:

  1. links simbólicos só podem ser resolvidos se o que eles devem linkar já está criado, o que obviamente nem sempre é o caso. Os comandos realpath e readlink têm opções para explicar isso.
  2. um diretório em um caminho pode mais tarde se tornar um link simbólico, o que significa que o caminho não é mais canonical . Portanto, o conceito depende do tempo (ou do ambiente).
  3. mesmo no caso ideal, quando todos os links simbólicos podem ser resolvidos, ainda pode haver mais de um canonical path para um arquivo, por dois motivos:
    • a partição que contém o arquivo pode ter sido montada simultaneamente ( ro ) em vários pontos de assembly.
    • pode haver links para o arquivo, o que significa essencialmente que o arquivo existe em vários diretórios diferentes.

Portanto, mesmo com a definição muito mais restritiva de canonical path , pode haver vários caminhos canônicos para um arquivo. Isso também significa que o qualificador canonical é um tanto inadequado, pois geralmente implica uma noção de exclusividade.

Isso expande uma breve discussão do tópico em uma resposta a outra pergunta semelhante no Bash: recuperar caminho absoluto dado relativo

Minha conclusão é que o realpath é melhor projetado e muito mais flexível que o readlink . O único uso de readlink que não é coberto pelo realpath é a chamada sem opção retornando o valor de um link simbólico.

Para diretórios, dirname é ativado para ../ e retorna ./ .

A function do nolan6000 pode ser modificada para corrigir isso:

 get_abs_filename() { # $1 : relative filename if [ -d "${1%/*}" ]; then echo "$(cd ${1%/*}; pwd)/${1##*/}" fi } 

Eu coloquei o seguinte script no meu sistema e eu chamo-o como um alias bash para quando eu quero pegar rapidamente o caminho completo para um arquivo no diretório atual:

 #!/bin/bash /usr/bin/find "$PWD" -maxdepth 1 -mindepth 1 -name "$1" 

Não tenho certeza porque, no OS X quando chamado por um script “$ PWD” se expande para o caminho absoluto. Quando o comando find é chamado na linha de comando, isso não acontece. Mas faz o que eu quero … curtir.

O dogbane responde com a descrição do que está acontecendo:

 #! /bin/sh echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")" 

Explicação:

  1. Este script obtém o caminho relativo como argumento "$1"
  2. Então nós obtemos a parte do dirname desse caminho (você pode passar dir ou arquivo para este script): dirname "$1"
  3. Então nós cd "$(dirname "$1") neste diretório relativo e obtemos o caminho absoluto para ele executando o comando pwd shell
  4. Depois disso, acrescentamos basename ao caminho absoluto: $(basename "$1")
  5. Como passo final, fazemos echo
 #! /bin/bash file="$@" realpath "$file" 2>/dev/null || eval realpath $(echo $file | sed 's/ /\\ /g') 

Isso compensa as deficiências do caminho realpath , armazena-o em um fullpath shell script. Agora você pode ligar:

 $ cd && touch a\ a && rm A 2>/dev/null $ fullpath "aa" /home/user/aa $ fullpath ~/a\ a /home/user/aa $ fullpath A A: No such file or directory. 

Esta não é uma resposta para a pergunta, mas para quem faz o script:

 echo `cd "$1" 2>/dev/null&&pwd||(cd "$(dirname "$1")";pwd|sed "s|/*\$|/${1##*/}|")` 

Ele manipula / .. ./ etc corretamente. Eu também parece trabalhar no OSX

Uma alternativa para obter o caminho absoluto em Ruby :

realpath() {ruby -e "require 'Pathname'; puts Pathname.new('$1').realpath.to_s";}

Funciona sem argumentos (pasta atual) e arquivo relativo e absoluto ou caminho de pasta como um argumento.

Ei pessoal, eu sei que é um tópico antigo, mas estou apenas postando isso para referência a qualquer outra pessoa que tenha visitado isso como eu. Se eu entendi a pergunta corretamente, acho que o comando locate $filename . Exibe o caminho absoluto do arquivo fornecido, mas somente se existir.