comando bash / fish para imprimir o caminho absoluto em um arquivo


448

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

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

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

#!/usr/bin/python
# Author: Diggory Hardy <diggory.hardy@gmail.com>
# 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)

Eu diria que a resposta de @ DennisWilliamson (usando a opção -m) é superior por (geralmente) ser mais portátil e trabalhar com arquivos que não existem.
TTT

Ou a resposta de Flimm; ambos são boas soluções. O de Bannier, no entanto, responde melhor à minha pergunta original.
Dhardy

3
Usuários OSX: veja esta resposta
Ian

Respostas:


561

Tente realpath.

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

133
+1, programa muito bom. Mas observe que é um comando externo (não um shell embutido) e pode não estar presente em todos os sistemas por padrão, ou seja, pode ser necessário instalá-lo. No Debian, ele reside em um pacote separado com o mesmo nome.
Roman Cheplyaka


1
@ Limm: ainda lhe dará um caminho válido dado algum caminho relativo. Se você quiser saber sobre a existência de arquivos, use outra ferramenta como, por exemplo, test.
Benjamin Bannier

14
@ Dalin: Tente instalar o coreutils. O binário é chamado grealpath.
Toni Penya-Alba

5
Esta ferramenta foi introduzida no GNU coreutils 8.15 (em 2012), por isso é bastante nova.
marbu

317

Experimente o readlinkque resolverá os links simbólicos:

readlink -e /foo/bar/baz

50
Prefiro usar '-f' em vez de '-e' para que possamos ver o caminho absoluto de um arquivo inexistente.
Hluk

5
Parece o mais simples para mim, além de portátil. O readlink é a porta do gnu coreutils, por isso será instalada em praticamente qualquer distribuição linux, exceto em algumas distribuídas.
catphive 16/11/10

14
Parece-me que -mé a melhor opção para usar.
Matt Joiner

1
@ Dalin: /usr/bin/readlinkem Mavericks. Ou você quis dizer que essas opções não são encontradas no OS X? Parece ser uma versão BSD atrofiada ...: p
iconoclast

14
@iconoclast Essas opções não estão disponíveis em OSX, e de acordo com o BSD readlinkmanpage: only the target of the symbolic link is printed. If the given argument is not a symbolic link, readlink will print nothing and exit with an error, então readlink não funcionará para esta finalidade no OSX
SnoringFrog

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

8
Não se esqueça de citar todas as coisas. Se você não entender o porquê, tente seu programa em um arquivo a b(dois espaços entre a e b, SO come um deles).
Roman Cheplyaka

1
Também tente isso em "/" se transforma em "///"
George

35
a única solução POSIX tão longe que não exige que você escrever e compilar um arquivo executável desde readlink e realpath não são POSIX
Ciro Santilli郝海东冠状病六四事件法轮功

25
Eu costumava function realpath { echo $(cd $(dirname $1); pwd)/$(basename $1); }me tornar um realpathcomando (resposta atualmente aceita) no OS X. Obrigado!
James Bedford

1
Eu usei esse método para definir nomes de arquivos de log para scripts:LOG=$(echo $(cd $(dirname $0); pwd)/$(basename $0 .sh).log)
DrStrangepork

84

Esqueça readlinkerealpath quais podem ou não estar instalados no seu sistema.

Expandindo a resposta do dogbane acima aqui, ele é expresso como uma função:

#!/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 Bash embutido pwd comando imprime o caminho absoluto do diretório atual quando chamado sem argumentos.

Por que gosto desta solução?

É portátil e não requer nenhum readlinkou realpathque geralmente não existe em uma instalação padrão de uma determinada distribuição Linux / Unix.

E se dir não existir?

Conforme indicado acima, a função falhará e será impressa no stderr se o caminho do diretório fornecido não existir. Pode não ser o que você deseja. Você pode expandir a função 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 uma das diretivas pai não existir.

Como você lida com os caracteres '..' ou '.' na entrada?

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

/Users/bob/Documents/..

Se você quiser resolver o '..', 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
}

Boa função, mas aparentemente você não entendeu a pergunta. Eu queria algo para uso interativo, não scripts.
dhardy

3
Gostaria de acrescentar que realpathvem do pacote coreutils, que também contém ferramentas como who, touchou cat. É um conjunto bastante padronizado de ferramentas GNU, para que você possa ter certeza de que ele está instalado em quase todas as máquinas baseadas em Linux. Dito isso, você está certo: há 2 problemas com ele: i) você provavelmente sentirá falta dele na instalação padrão em sistemas não-GNU unix (como o OpenBSD) ii) essas ferramentas foram introduzidas na versão 8.15 (portanto, é bastante nova a partir de 2012), portanto você sentirá falta de sistemas estáveis ​​a longo prazo (como o RHEL 6).
marbu

1
Bem, parece pelo menos que o Continuum Analytics (criadores da distribuição Anaconda Python ) gostou desta resposta. Ele é implementado (com uma referência vinculando aqui) em seu script "ativar" , usado para ativar ambientes virtuais criados pela ferramenta conda. Então ... bom!
Wjv 6/03

1
Isso funciona apenas para diretórios (já que não há mais) e não pode lidar com ".." e ".". Veja a minha resposta que deve lidar com tudo: stackoverflow.com/a/23002317/2709
Alexander Klimetschek

3
O @marbu realpath não está presente no Ubuntu 14.04 ou no Debian Wheezy. Pode se tornar um pouco padrão ao longo do tempo, mas certamente não é agora. Observe também que o OP não disse nada sobre linux ou GNU. O Bash é mais amplamente usado que isso.
mc0e

76
$ readlink -m FILE
/path/to/FILE

Isso é melhor que readlink -e FILEou realpathporque funciona mesmo que o arquivo não exista.


Agradável. Isso também funciona em arquivos / diretórios que existem, mas não são links simbólicos.
quietmint

Eu tenho o readlink disponível no OS X 10.9 (Mavericks), então essa é definitivamente a melhor resposta aqui.
precisa

6
@KennethHoste: a -mopção não está disponível no Mavericks.
Iconoclast

2
Definitivamente, não está em uma instalação padrão do OSX.
21415 Fran Franzozo

obras no Linux (CentOS) obrigado, porque realpath não existia para ele
jimh

65

Esse caminho relativo para a função de shell do conversor de caminho absoluto

  • não requer utilitários (apenas cde pwd)
  • funciona para diretórios e arquivos
  • alças ..e.
  • lida com espaços em dir ou nomes de arquivos
  • requer que arquivo ou diretório exista
  • não retorna nada se nada existir no caminho especificado
  • lida com caminhos absolutos como entrada (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 a caixa do arquivo.

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, incluindo scripts shell que desejam ter dependências mínimas, coloquei essa solução de script aqui, para encontrá-la mais tarde :)


Obrigado. Isso é o que eu vou com a OSX
Greg

Isso não funciona para diretórios com espaços, ou seja: $ abspath 'a b c/file.txt'Isso cdocorre porque o argumento para não é citado. Para superar isso, em vez de citar o argumento inteiro para echo(existe uma razão para essas aspas?), Cite apenas os argumentos do caminho. Ou seja, substitua echo "$(cd ${1%/*}; pwd)/${1##*/}"por echo $(cd "${1%/*}"; pwd)/${1##*/}.
george

@george Obrigado por notar, eu consertei.
Alexander Klimetschek

1
Eu olhei por dezenas de soluções semi-engatilhadas e essa definitivamente parece ser a solução mais concisa e precisa do bash. Você pode reduzi-lo ainda mais substituindo-o echo "$(cd "$1"; pwd)"por (cd "$1"; pwd). Não há necessidade de eco aqui.
6

@Six True, atualizado. Os colchetes ( ... )ainda são necessários, o que cdacontece em um subshell, pois não queremos alterar o diretório de trabalho de nenhum chamador de abspath.
Alexander Klimetschek 27/03

24

O findcomando pode ajudar

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

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

find $PWD

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


1
eu não grok este comentário na primeira leitura; o ponto principal aqui é que, se você der um caminho absoluto ao comando find, ele produzirá resultados em um caminho absoluto. portanto, usar $ PWD é o caminho absoluto de onde você está para obter a saída como absoluta.
simbo1905

4
Se você confiar PWD, você pode simplesmente usar $PWD/$filename.
jonny

Essa abordagem pode ser aprimorada com o uso de -maxdepth 1. Também não se esqueça de citar - os possíveis problemas de segurança aqui, dependendo de como você o usa.
Mc0e

1
@jonny: $ PWD / $ filename falhará se $ filename já for absoluto.
Mc0e

Isso falhará se o nome do arquivo for fornecido como "./filename.txt" (com ponto e barra à esquerda).
Mark Stosberg

15

Se você não possui utilitários readlink ou realpath, pode usar a seguinte função, que funciona no bash e no zsh (não tem 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 função python os.path.abspath).

Infelizmente abspath ./../somefilenão se livra dos pontos.


1
Parece portátil para mim. Por outro lado, quebrará, por exemplo, um nome de arquivo contendo uma nova linha.
Roman Cheplyaka

1
Para melhorar ainda mais, substitua echo "$1"por printf "%s\n" "$1"(o mesmo com o segundo eco). echopode interpretar barras invertidas dentro de seus argumentos.
Roman Cheplyaka

Mais uma vez, você está certo! Realmente, o comportamento do comando echo no zsh é diferente do bash.
Hluk

1
Estritamente falando, no comportamento POSIX de eco é indefinido se os argumentos contiverem barras invertidas. No entanto, é definido na extensão XSI (ou seja, o eco deve interpretar as seqüências de escape). Mas ambos bash e zsh são soo longe até mesmo de conformidade POSIX ...
Roman Cheplyaka

Desculpe, mas não entendi o motivo. Eu já forneci uma resposta como script com mais recursos que isso e não precisa ser usado em um script de shell.
dhardy

10

Aqui está uma função apenas do zsh que eu gosto por sua compacidade. Ele usa o modificador de expansão 'A' - consulte zshexpn (1).

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

Uau, que é uma solução elegante !!
ShellFish 19/04/2014

6

Geralmente não há tal coisa como o absolute path para um arquivo (o que significa demonstração de que pode haver mais de um em geral, daí o uso do artigo definido o não é o caso). An absolute pathé qualquer caminho que começa na raiz "/" e designa um arquivo sem ambiguidade, independentemente do diretório de trabalho (veja, por exemplo, wikipedia ).

A relative pathé um caminho que deve ser interpretado a partir de outro diretório. Pode ser o diretório de trabalho se for umrelative path sendo manipulado por um aplicativo (embora não necessariamente). Quando está em um link simbólico em um diretório, geralmente se destina a ser 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 pouco impermeável a alterações na estrutura de ligação, mas isso não é necessariamente necessário ou mesmo desejável. Algumas pessoas chamam canonical path(ou canonical file nameou resolved path) um absolute pathem que todos os links simbólicos foram resolvidos, ou seja, foram substituídos por um caminho para onde quer que eles estejam. Os comandos realpathe os readlinkdois procuram um caminho canônico, mas só realpathtêm 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 exige várias observações:

  1. links simbólicos só podem ser resolvidos se o que eles deveriam vincular já estiver criado, o que obviamente nem sempre é o caso. Os comandos realpathe readlinktem opções para explicar isso.
  2. posteriormente, um diretório em um caminho pode 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 pathpara um arquivo, por dois motivos:
    • a partição que contém o arquivo pode ter sido montada simultaneamente (ro ) em vários pontos de montagem.
    • pode haver links físicos para o arquivo, significando 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 singularidade.

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

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


Não me importo de ter o voto negativo, mas gosto de entender por que, para aprender algo técnico ou sobre o que é considerado uma prática adequada no site. Bem ... pelo menos isso me levou a me registrar no Meta Stack Overflow para entender melhor a sociologia local. Eu recomendo. - Ainda assim, se alguém tiver uma opinião sobre a inadequação da minha resposta, estou interessado.
Babou 20/05

1
(a) a pergunta tem uma resposta válida de 2 + 1/2 anos atrás, (b) existe um caminho absoluto (único) correspondente a um caminho relativo (que é o que eu queria), embora talvez não seja um arquivo como você apontou. BTW quaisquer comentários sobre realpathvs readlinkou mesmo sobre links simbólicos são excedentes ao que pedi.
Dhardy 28/05

1
Segundo comentário: respostas curtas são muitas vezes mais úteis do que longas palestras. O estouro de pilha é geralmente usado para fazer perguntas específicas.
Dhardy 28/05

5
1- o fato de uma pergunta ter uma resposta válida por tanto tempo não significa que ela está encerrada. As perguntas não se destinam apenas ao uso pessoal. De fato, existem emblemas para respostas que coletam mais votos do que a resposta validada. E com novas ferramentas, a "melhor" resposta pode mudar com o tempo. Muitas pessoas usam respostas antigas acessadas através da pesquisa na web.
Babou 29/05

1
2 - Você insiste que existe um caminho absoluto (único) correspondente a um caminho relativo . Embora eu adivinhe o que você quer dizer com "correspondente", isto é, um caminho derivado do original através de um determinado conjunto de transformações, sua declaração ainda está incorreta. Existe um caminho canônico "correspondente" exclusivo. A palavra canonicalrefere-se precisamente a essa singularidade em relação a um conjunto de transformações. Mas, por exemplo, / dev / cdrom é um caminho absoluto perfeitamente bom, embora seja realmente um link para / dev / sr0. Ambos apontam para o mesmo dispositivo. Minha resposta original apontou para um artigo relevante da web.
Babou 29/05

5

A dogbane resposta com a descrição do que está por vir:

#! /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 temos o dirname parte desse caminho (você pode passar dir ou arquivo para este script):dirname "$1"
  3. Então, cd "$(dirname "$1")entramos nesse diretório relativo e obtemos o caminho absoluto para ele executandopwd comando shell
  4. Depois disso, acrescentamos o nome da base ao caminho absoluto:$(basename "$1")
  5. Como etapa final, echoele

2

Para diretórios dirnameé acionado ../e retorna./ .

A função do nolan6000 pode ser modificada para corrigir isso:

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

1
Bem-vindo ao SO! Para pequenas alterações e observações, adicionar um comentário a uma resposta existente pode ser mais apropriado do que adicionar uma resposta, especialmente se essa resposta copiada não possuir muitas outras informações da resposta original. Depois de coletar um pouco mais de reputação, você poderá adicionar comentários às respostas de outras pessoas. Por enquanto, tente coletar reputação fazendo perguntas únicas e valiosas ou fornecendo respostas independentes e boas.
Cfi

1
Como você está se referindo à resposta de nolan6000, observe que o dhardy do pôster já comentou que ele não aceitaria a resposta de nolan6000 porque ele não estava procurando por um script. Portanto, estritamente falando, sua resposta não responde à pergunta.
Cfi

a função pode ser adicionada .profilee, portanto, disponível para uso interativo.
adib 19/03/2015

Isso não funcionará se $1for um nome de arquivo simples: get_abs_filename foo-> nothing (ou <current_dir>/foo/foose foofor um diretório).
ivan_pozdeev

2

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

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

ele lida com / .. ./ etc corretamente. Eu também parece trabalhar no OSX


2

Coloquei o seguinte script no meu sistema e chamo-o como um alias do bash para quando eu quiser 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 sei por que, mas no OS X quando chamado por um script "$ PWD" se expande para o caminho absoluto. Quando o comando find é chamado na linha de comando, não é. Mas faz o que eu quero ... curtir.


$PWDsempre tem o caminho absoluto do diretório de trabalho. Além disso, findnão tolerará barras, então você não está respondendo à pergunta conforme solicitado. Uma maneira mais rápida de fazer o que você está procurando é simplesmenteecho "$PWD/$1"
Adam Katz

2

O mais simples se você deseja usar apenas os recursos internos é provavelmente:

find `pwd` -name fileName

Apenas duas palavras extras para digitar, e isso funcionará em todos os sistemas unix, bem como no OSX.


Eu acrescentaria -maxdepth 1antes -name(supondo que o arquivo esteja no diretório atual). Sem isso, ele funcionará com arquivos em qualquer subdiretório do pwd, mas não com outros.
Walter Tross

Na verdade, você está certo, a pergunta especificou que o arquivo estaria no diretório atual. O que você quer dizer com "mas não com os outros"?
Arj

Quero dizer find, não encontrará um arquivo fora da árvore de diretórios abaixo pwd. Por exemplo, se você tentar find . -name ../existingFile, ele falhará.
Walter Tross

É justo, sim, isso pressupõe que você deseja imprimir o caminho completo de um arquivo no seu cwd (conforme a pergunta original) ou em qualquer lugar da árvore do cwd (não na pergunta original).
Arj

1
#! /bin/bash

file="$@"
realpath "$file" 2>/dev/null || eval realpath $(echo $file | sed 's/ /\\ /g')

Isso compensa as deficiências de realpath, armazene-o em um script de shell fullpath. Agora você pode ligar para:

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

O que isso resolve? realpath "foo bar"-> /home/myname/foo bar. Se você não pode se dar ao trabalho de citar argumentos da linha de comando, isso não realpathé culpa.
precisa saber é o seguinte

Concordado, é mais um invólucro de conveniência.
ShellFish

1

Uma alternativa para obter o caminho absoluto no Ruby :

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

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


0

Responder com Homebrew

realpathé a melhor resposta, mas se você não o tiver instalado, execute primeiro o brew install coreutilsque instalará o coreutils com muitas funções impressionantes. Escrever uma função personalizada e exportar é muito trabalho e risco de erro para algo como isto, aqui estão duas linhas:

$ brew install coreutils
$ realpath your-file-name.json 

-1

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


4
locateé um utilitário para procurar arquivos / caminhos por nome. Pode fazer o trabalho, mas também pode encontrar outras correspondências e pode não encontrar um arquivo existente sem chamar updatedbprimeiro como root.
Dhardy
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.