comando bash / fish para imprimir la ruta absoluta a un archivo


448

Pregunta: ¿hay un comando simple sh / bash / zsh / fish / ... para imprimir la ruta absoluta de cualquier archivo que lo alimente?

Caso de uso: Estoy en el directorio /a/by me gustaría para imprimir la ruta completa al archivo cen la línea de comandos para que pueda pegar fácilmente en otro programa: /a/b/c. Simple, pero un pequeño programa para hacer esto probablemente podría ahorrarme aproximadamente 5 segundos cuando se trata de manejar caminos largos, lo que al final se suma. Entonces me sorprende que no puedo encontrar una utilidad estándar para hacer esto, ¿realmente no hay ninguna?

Aquí hay una implementación de muestra, 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)

Yo diría que la respuesta de @ DennisWilliamson (usando la opción -m) es superior para (generalmente) ser más portátil y trabajar con archivos que no existen.
TTT

O la respuesta de Flimm; Ambos son buenas soluciones. Sin embargo, Bannier responde mejor a mi pregunta original.
dhardy

3
Usuarios de OSX: ver esta respuesta
Ian

Respuestas:


562

Tratar realpath.

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

133
+1, muy buen programa. Pero tenga en cuenta que es un comando externo (no un shell incorporado) y puede no estar presente en todos los sistemas de forma predeterminada, es decir, puede que necesite instalarlo. En Debian reside en un paquete separado con el mismo nombre.
Roman Cheplyaka


1
@Flimm: aún te dará una ruta válida dada alguna ruta relativa. Si desea saber sobre la existencia de archivos, use otra herramienta como, por ejemplo, prueba.
Benjamin Bannier

14
@Dalin: intente instalar coreutils. El binario se llama grealpath.
Toni Penya-Alba

55
Esta herramienta se introdujo en GNU coreutils 8.15 (en 2012), por lo que es bastante nueva.
marbu

318

Pruebe readlinkcuál resolverá los enlaces simbólicos:

readlink -e /foo/bar/baz

50
Preferiría usar '-f' en lugar de '-e' para que podamos ver la ruta absoluta de un archivo inexistente.
hluk

55
Esto me parece el más simple, a la vez que portátil. readlink es el puerto de gnu coreutils, por lo que se instalará en casi cualquier distribución de Linux, excepto en algunas integradas.
catphive

14
Me parece que esa -mes la mejor opción para usar.
Matt Joiner

1
@Dalin: /usr/bin/readlinken Mavericks. ¿O quiso decir que esas opciones no se encuentran en OS X? Parece ser una versión BSD atrofiada ...: p
iconoclasta

14
@iconoclast Estas opciones no están disponibles en OSX, y según la página de readlinkmanual de BSD : 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 errorpor lo tanto, readlink no funcionará para este propósito en OSX
SnoringFrog

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

8
No olvides citar todas las cosas. Si no entiende por qué, pruebe su programa en un archivo a b(dos espacios entre a y b, SO se come uno de ellos).
Roman Cheplyaka

1
también intente esto en "/" se convierte en "///"
George

35
la única solución POSIX hasta ahora que no requiere que escriba y compile un ejecutable ya que readlink y realpath no son POSIX
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

25
Solía function realpath { echo $(cd $(dirname $1); pwd)/$(basename $1); }hacerme un realpathcomando (respuesta actualmente aceptada) en OS X. ¡Gracias!
James Bedford

1
Utilicé este método para definir nombres de archivo de registro para scripts:LOG=$(echo $(cd $(dirname $0); pwd)/$(basename $0 .sh).log)
DrStrangepork

84

Olvídate readlinky realpathqué puede o no estar instalado en tu sistema.

Ampliando la respuesta anterior de dogbane aquí se expresa como una función:

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

entonces puedes usarlo así:

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

¿Cómo y por qué funciona?

La solución explota el hecho de que el pwdcomando incorporado Bash imprimirá la ruta absoluta del directorio actual cuando se invoque sin argumentos.

¿Por qué me gusta esta solución?

Es portátil y no requiere ninguno readlinko realpathque a menudo no existe en una instalación predeterminada de una distribución de Linux / Unix dada.

¿Qué pasa si el directorio no existe?

Como se indicó anteriormente, la función fallará e imprimirá en stderr si la ruta de directorio dada no existe. Esto puede no ser lo que quieres. Puede expandir la función para manejar esa situación:

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

Ahora devolverá una cadena vacía si uno de los directorios principales no existe.

¿Cómo manejas el final '..' o '.' en entrada?

Bueno, da un camino absoluto en ese caso, pero no uno mínimo. Se verá así:

/Users/bob/Documents/..

Si desea resolver el '...', deberá crear el 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
}

Buena función, pero aparentemente no entendiste la pregunta. Quería algo para uso interactivo, no scripts.
Dhardy

3
Me gustaría agregar que realpathproviene del paquete coreutils, que también contiene herramientas como who, toucho cat. Es un conjunto bastante estándar de herramientas GNU, por lo que puede estar seguro de que está instalado en casi cualquier máquina basada en Linux. Dicho esto, tiene razón: hay 2 problemas con él: i) probablemente lo perderá en la instalación predeterminada en sistemas unix que no son GNU (como OpenBSD) ii) esta herramienta se introdujo en la versión 8.15 (por lo que es bastante nueva, desde 2012), por lo que la echará de menos en sistemas estables a largo plazo (como RHEL 6).
marbu

1
Bueno, parece que al menos a Continuum Analytics (fabricantes de la distribución Anaconda Python ) le gustó esta respuesta. Se implementa (con un enlace de referencia aquí) en su secuencia de comandos "activar" , que se utiliza para activar entornos virtuales creados por la herramienta conda. Entonces ... ¡buena!
wjv

1
Esto solo funciona para directorios (ya que no hay más) y no puede hacer frente a ".." y ".". Vea mi respuesta que debería manejar todo: stackoverflow.com/a/23002317/2709
Alexander Klimetschek

3
@marbu realpath no está presente en Ubuntu 14.04 o en Debian Wheezy. Puede volverse algo estándar con el tiempo, pero ciertamente no lo es ahora. También tenga en cuenta que el OP no dijo nada sobre Linux o GNU. Bash se usa más ampliamente que eso.
mc0e

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

Esto es mejor que readlink -e FILEo realpath, porque funciona incluso si el archivo no existe.


Agradable. Esto también funciona en archivos / directorios que existen pero no son enlaces simbólicos.
quietmint

Tengo readlink disponible en OS X 10.9 (Mavericks), por lo que esta es definitivamente la mejor respuesta aquí.
Kenneth Hoste

66
@KennethHoste: la -mopción no está disponible en Mavericks.
iconoclasta

2
Definitivamente no está en una instalación OSX PREDETERMINADA.
Fran Marzoa

funciona en Linux (CentOS) gracias, porque Realpath no existía para ello
Jim

65

Esta ruta relativa a la función de shell del convertidor de ruta absoluta

  • no requiere utilidades (solo cdy pwd)
  • funciona para directorios y archivos
  • asas ..y.
  • maneja espacios en dir o nombres de archivos
  • requiere que exista ese archivo o directorio
  • no devuelve nada si no existe nada en la ruta dada
  • maneja rutas absolutas como entrada (las pasa esencialmente)

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
}

Muestra:

# 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: Esto se basa en las respuestas de nolan6000 y bsingh , pero corrige el caso del archivo.

También entiendo que la pregunta original era sobre una utilidad de línea de comando existente. Pero dado que esta parece ser LA pregunta sobre stackoverflow para eso, incluidos los scripts de shell que desean tener dependencias mínimas, puse esta solución de script aquí, para que pueda encontrarla más tarde :)


Gracias. Eso es lo que uso en OSX
Greg

Esto no funciona para directorios con espacios, es decir: $ abspath 'a b c/file.txt' Eso se debe a que el argumento cdno se cita. Para superar esto, en lugar de citar todo el argumento para echo(¿hay alguna razón para estas citas?), Cite solo los argumentos de ruta. Es decir, reemplazar echo "$(cd ${1%/*}; pwd)/${1##*/}"con echo $(cd "${1%/*}"; pwd)/${1##*/}.
george

@george Gracias por señalar, lo arreglé.
Alexander Klimetschek

1
Miré a través de docenas de soluciones a medias y esta definitivamente parece ser la solución más concisa y precisa de solo bash. Podrías adelgazarlo aún más reemplazándolo echo "$(cd "$1"; pwd)"con (cd "$1"; pwd). No hay necesidad de eco aquí.
Seis

@Six True, actualizado. Las llaves ( ... )todavía son necesarias, por lo que cdsucede en una subshell, ya que no queremos cambiar el directorio de trabajo para ninguna persona que llame abspath.
Alexander Klimetschek

24

El findcomando puede ayudar

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

Enumera todos los archivos en o debajo del directorio actual con nombres que coinciden con el patrón. Puede simplificarlo si solo obtiene unos pocos resultados (por ejemplo, un directorio cerca de la parte inferior del árbol que contiene algunos archivos), solo

find $PWD

Lo uso en Solaris 10, que no tiene las otras utilidades mencionadas.


1
No asimilé este comentario en la primera lectura; El punto clave aquí es que si le das una ruta absoluta al comando find, entonces generará resultados en la ruta absoluta. por lo tanto, el uso de $ PWD es la ruta absoluta de dónde se encuentra para obtener el resultado como absoluto.
simbo1905

44
Si confía PWD, simplemente podría usar $PWD/$filename.
jonny

Este enfoque se puede mejorar con el uso de -maxdepth 1. Además, no se olvide de las citas, los posibles problemas de seguridad aquí, dependiendo de cómo lo use.
mc0e

1
@jonny: $ PWD / $ filename falla si $ filename ya es absoluto.
mc0e

Esto falla si el nombre del archivo se da como "./filename.txt" (con punto y barra).
Mark Stosberg

15

Si no tiene las utilidades readlink o realpath, puede usar la siguiente función que funciona en bash y zsh (no estoy seguro del resto).

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

Esto también funciona para archivos inexistentes (al igual que la función python os.path.abspath).

Lamentablemente abspath ./../somefileno se deshace de los puntos.


1
A mí me parece portátil. Por otro lado, se romperá, por ejemplo, en un nombre de archivo que contenga una nueva línea.
Roman Cheplyaka

1
Para mejorar aún más, reemplace echo "$1"con printf "%s\n" "$1"(lo mismo con el segundo eco). echopuede interpretar barras invertidas dentro de sus argumentos.
Roman Cheplyaka

De nuevo, tienes razón! Realmente, el comportamiento del comando echo en zsh es diferente del bash.
hluk

1
Estrictamente hablando, bajo POSIX el comportamiento del eco no está definido si los argumentos contienen barras invertidas. Sin embargo, se define bajo la extensión XSI (es decir, echo debe interpretar las secuencias de escape). Pero tanto bash como zsh están muy lejos incluso del cumplimiento de POSIX ...
Roman Cheplyaka

Lo siento, pero no veo el punto. Ya proporcioné una respuesta como script con más características que esta y no necesita ser utilizada dentro de un script de shell.
dhardy

10

Aquí hay una función de solo zsh que me gusta por su compacidad. Utiliza el modificador de expansión 'A'; consulte zshexpn (1).

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

¡Guau, esa es una solución elegante!
ShellFish

6

Generalmente, no hay tal cosa como la absolute path de un archivo (este medio declaración de que no puede haber más de uno, en general, de ahí el uso del artículo definido el no es el caso). Una absolute pathes cualquier ruta que comienza desde la raíz "/" y designa un archivo sin ambigüedad independientemente del directorio de trabajo (ver, por ejemplo, wikipedia ).

A relative pathes una ruta que se debe interpretar a partir de otro directorio. Puede ser el directorio de trabajo si está relative pathsiendo manipulado por una aplicación (aunque no necesariamente). Cuando está en un enlace simbólico en un directorio, generalmente está destinado a ser relativo a ese directorio (aunque el usuario puede tener otros usos en mente).

Por lo tanto, una ruta absoluta es solo una ruta relativa al directorio raíz.

Una ruta (absoluta o relativa) puede o no contener enlaces simbólicos. Si no lo hace, también es algo impermeable a los cambios en la estructura de enlace, pero esto no es necesariamente necesario ni deseable. Algunas personas llaman canonical path(o canonical file nameo resolved path) una absolute pathen la que se han resuelto todos los enlaces simbólicos, es decir, han sido sustituidos por un camino a la Whetever que enlazan a. Los comandos realpathy readlinkambos buscan una ruta canónica, pero solo realpathtiene una opción para obtener una ruta absoluta sin molestarse en resolver enlaces simbólicos (junto con varias otras opciones para obtener varios tipos de rutas, absolutas o relativas a algún directorio).

Esto requiere varios comentarios:

  1. los enlaces simbólicos solo pueden resolverse si lo que se supone que deben vincular ya está creado, lo que obviamente no siempre es así. Los comandos realpathy readlinktienen opciones para dar cuenta de eso.
  2. un directorio en una ruta puede luego convertirse en un enlace simbólico, lo que significa que la ruta ya no lo es canonical. Por lo tanto, el concepto depende del tiempo (o del entorno).
  3. incluso en el caso ideal, cuando se pueden resolver todos los enlaces simbólicos, puede haber más de uno canonical pathen un archivo, por dos razones:
    • la partición que contiene el archivo puede haberse montado simultáneamente ( ro) en varios puntos de montaje.
    • puede haber enlaces duros al archivo, lo que significa que esencialmente el archivo existe en varios directorios diferentes.

Por lo tanto, incluso con la definición mucho más restrictiva de canonical path, puede haber varias rutas canónicas a un archivo. Esto también significa que el calificador canonicales algo inadecuado, ya que generalmente implica una noción de unicidad.

Esto amplía una breve discusión del tema en una respuesta a otra pregunta similar en Bash: recuperar la ruta absoluta dada relativa

Mi conclusión es que realpathestá mejor diseñado y es mucho más flexible que readlink. El único uso readlinkque no está cubierto realpathes la llamada sin opción que devuelve el valor de un enlace simbólico.


No me importa que me voten negativamente, pero me gusta entender por qué para poder aprender algo, ya sea técnico o sobre lo que se considera una práctica adecuada en el sitio. Bueno ... al menos me indujo a registrarme en Meta Stack Overflow para comprender mejor la sociología local. Lo recomiendo - Aún así, si alguien tiene una opinión sobre lo inapropiado de mi respuesta, estoy interesado.
babou

1
(a) la pregunta tiene una respuesta válida de hace 2 + 1/2 años, (b) hay una ruta absoluta (única) correspondiente a una ruta relativa (que es lo que quería), aunque tal vez no a un archivo como usted señaló. Por cierto, cualquier comentario sobre realpathvs readlinko incluso sobre enlaces simbólicos excede lo que pregunté.
Dhardy

1
Segundo comentario: las respuestas cortas son a menudo más útiles que las largas conferencias. El desbordamiento de pila generalmente se usa para hacer preguntas específicas.
Dhardy

55
1- el hecho de que una pregunta haya tenido una respuesta válida por el tiempo que sea no significa que esté cerrada. Las preguntas no están destinadas solo para uso personal. De hecho, hay distintivos para las respuestas que recogen más votos que la respuesta validada. Y con nuevas herramientas, la "mejor" respuesta puede cambiar con el tiempo. Muchas personas usan respuestas antiguas a las que se accede mediante la búsqueda web.
babou

1
2 - Insiste en que hay una ruta absoluta (única) correspondiente a una ruta relativa . Si bien supongo lo que quiere decir con "correspondiente", es decir, una ruta derivada del original a través de un conjunto dado de transformaciones, su afirmación sigue siendo incorrecta. Hay una ruta canónica "correspondiente" única. La palabra se canonicalrefiere precisamente a esta singularidad en relación con un conjunto de transformaciones. Pero, por ejemplo, / dev / cdrom es una ruta absoluta perfectamente buena, aunque en realidad es un enlace a / dev / sr0. Ambos apuntan al mismo dispositivo. Mi respuesta original apuntaba a un artículo web relevante.
babou

5

La dogbane respuesta con la descripción de lo que viene:

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

Explicación:

  1. Este script obtiene una ruta relativa como argumento "$1"
  2. Entonces obtenemos dirname parte de ese camino (se puede pasar a cualquiera dir o archivo a esta secuencia de comandos):dirname "$1"
  3. Entonces nosotros cd "$(dirname "$1") en este directorio relativo y obtenemos una ruta absoluta para él ejecutando el pwdcomando de shell
  4. Después de eso agregamos basename a la ruta absoluta:$(basename "$1")
  5. Como paso final que echose

2

Para los directorios dirnamese dispara ../y regresa ./.

La función de nolan6000 se puede modificar para arreglar eso:

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

1
Bienvenido a SO! Para pequeños cambios y comentarios, agregar un comentario a una respuesta existente podría ser más apropiado que agregar una respuesta, especialmente si esta respuesta copiada carece de mucha otra información de la respuesta original. Una vez que haya recogido un poco más de reputación, podrá agregar comentarios a las respuestas de otras personas. Por ahora, trate de ganar reputación haciendo preguntas únicas y valiosas o proporcionando respuestas independientes y buenas.
cfi

1
Como se está refiriendo a la respuesta de nolan6000, tenga en cuenta que el póster dhardy ya comentó que no aceptaría la respuesta de nolan6000 porque no está buscando un guión. Entonces, estrictamente hablando, su respuesta no responde la pregunta.
cfi

la función se puede agregar .profiley, por lo tanto, está disponible para uso interactivo.
adib

Esto no funcionará si $1es un nombre de archivo simple: get_abs_filename foo-> nada (o <current_dir>/foo/foosi fooes un directorio).
ivan_pozdeev

2

Esta no es una respuesta a la pregunta, sino para aquellos que hacen scripts:

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

maneja / .. ./ etc correctamente. También parece que trabajo en OSX


2

He colocado el siguiente script en mi sistema y lo llamo como un alias bash para cuando quiero tomar rápidamente la ruta completa a un archivo en el directorio actual:

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

No estoy seguro de por qué, pero en OS X cuando se llama por un script "$ PWD" se expande a la ruta absoluta. Cuando se llama al comando find en la línea de comando, no lo hace. Pero hace lo que quiero ... disfrutar.


$PWDsiempre tiene la ruta absoluta del directorio de trabajo. Además, findno tolerará cortes, por lo que no responderá la pregunta como se le preguntó. Una forma más rápida de hacer lo que estás buscando hacer sería simplementeecho "$PWD/$1"
Adam Katz el

2

Probablemente, la más simple si desea utilizar solo builtins es:

find `pwd` -name fileName

Solo dos palabras adicionales para escribir, y esto funcionará en todos los sistemas Unix, así como en OSX.


Agregaría -maxdepth 1antes -name(suponiendo que el archivo esté en el directorio actual). Sin eso, funcionará con archivos en cualquier subdirectorio de pwd, pero no con otros.
Walter Tross

De hecho, tiene razón, la pregunta especificó que el archivo estaría en el directorio actual. ¿Qué quieres decir con "pero no con los demás"?
Arj

Quiero decir findque no encontrarás un archivo fuera del árbol de directorios debajo pwd. Por ejemplo, si lo intentas find . -name ../existingFile, falla.
Walter Tross

Justo, sí, esto supondría que desea imprimir la ruta completa de un archivo en su cwd (según la pregunta original), o en cualquier lugar de su árbol cwd (no en la pregunta original).
Arj

1
#! /bin/bash

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

Esto compensa las deficiencias de realpathalmacenarlo en un script de shell fullpath. Ahora puedes llamar:

$ 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.

¿Qué resuelve? realpath "foo bar"-> /home/myname/foo bar. Si no puede molestarse en citar argumentos de la línea de comandos, no realpathes culpa suya.
ivan_pozdeev

De acuerdo, es más una envoltura de conveniencia.
ShellFish

1

Una alternativa para obtener el camino absoluto en Ruby :

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

Funciona sin argumentos (carpeta actual) y el archivo relativo y absoluto o la ruta de la carpeta como elemento.


0

Responde con Homebrew

realpathes la mejor respuesta, pero si no lo tiene instalado, primero debe ejecutar brew install coreutilslo que instalará coreutils con muchas funciones increíbles. Escribir una función personalizada y exportarla es demasiado trabajo y riesgo de error para algo como esto, aquí hay dos líneas:

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

-1

Hola chicos, sé que es un hilo viejo, pero solo estoy publicando esto para referencia a cualquier otra persona que haya visitado esto como yo. Si entendí la pregunta correctamente, creo que el locate $filenamecomando. Muestra la ruta absoluta del archivo suministrado, pero solo si existe.


44
locatees una utilidad para buscar archivos / rutas por nombre. Puede hacer el trabajo pero también puede encontrar otras coincidencias y puede que no encuentre un archivo existente sin llamar updatedbprimero como root.
dhardy
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.