¿Cómo obtengo el directorio absoluto de un archivo en bash?

81

He escrito un script bash que toma un archivo de entrada como argumento y lo lee.
Este archivo contiene algunas rutas (relativas a su ubicación) a archivos adicionales utilizados.

Me gustaría que el script vaya a la carpeta que contiene el archivo de entrada para ejecutar más comandos.

Entonces, ¿cómo obtengo la carpeta (y solo la carpeta) de un archivo de entrada? (En linux.)

BobMcGee
fuente
¿Está dando la ruta completa al archivo de entrada o solo la ruta relativa al directorio de trabajo actual?
dmh
dhm: la entrada da una ruta relativa, quiero una ruta absoluta menos el nombre de archivo.
BobMcGee

Respuestas:

148

Para obtener el uso de la ruta completa:

readlink -f relative/path/to/file

Para obtener el directorio de un archivo:

dirname relative/path/to/file

También puedes combinar los dos:

dirname $(readlink -f relative/path/to/file)

Si readlink -fno está disponible en su sistema, puede usar esto * :

function myreadlink() {
  (
  cd "$(dirname $1)"         # or  cd "${1%/*}"
  echo "$PWD/$(basename $1)" # or  echo "$PWD/${1##*/}"
  )
}

Tenga en cuenta que si solo necesita moverse a un directorio de un archivo especificado como una ruta relativa, no necesita saber la ruta absoluta, una ruta relativa es perfectamente legal, así que use:

cd $(dirname relative/path/to/file)

si desea volver (mientras se ejecuta el script) a la ruta original, utilice en pushdlugar de cdy popdcuando haya terminado.


* Si bien lo myreadlinkanterior es suficientemente bueno en el contexto de esta pregunta, tiene algunas limitaciones en relación con la readlinkherramienta sugerida anteriormente. Por ejemplo, no sigue correctamente un enlace a un archivo con diferente basename.

Chen Levy
fuente
17
Tenga en cuenta que readlink -f, lamentablemente, no funciona en OS X.
Emil Sit
No quiero la ruta completa, solo quiero la carpeta. Pero esto ofrece lo suficiente para ser útil: readlink -f relatedFileLocation | xargs dirname
BobMcGee
1
@EmilSit: tienes razón, de forma nativa. Pero puedes brew install coreutilsconseguirlo. stackoverflow.com/a/4031502/1022967 .
mpettis
3
No recomiende usar ${1%/*}o ${1##*/}como sustitutos de dirnamey basename. El primero se rompe si le da un nombre de archivo simple ( dirname foo.barsería, correctamente, ser .), y el segundo se rompe para un directorio que termina con una barra ( basename /foo/bar/, correctamente, sería bar).
Camilo Martin
1
@ChenLevy Algunas personas pueden inclinarse a usarlo por el simple hecho de ser más rápido (creo que basename/ dirnameno son incorporados), y lo verán funcionando al principio, pero de repente se romperá en algún momento en el futuro por aparentemente no ser bueno razón. Evitaría sugerirlo como una opción, o negaría que realmente no sea un sustituto a prueba de balas.
Camilo Martin
18

Eche un vistazo a la página de manual de realpath, yo uso eso y algo como:

CONTAININGDIR = $ (ruta real $ {FILEPATH% / *})

para hacer lo que parece que estás intentando hacer.

Richard Sitze
fuente
realpath me hace algo extraño donde resuelve enlaces simbólicos de
directorios
¡Gracias! Trabajó para mí en Mac OS
Dmitry Klochkov
6

Esto funcionará tanto para el archivo como para la carpeta:

absPath(){
    if [[ -d "$1" ]]; then
        cd "$1"
        echo "$(pwd -P)"
    else 
        cd "$(dirname "$1")"
        echo "$(pwd -P)/$(basename "$1")"
    fi
}
Jahid
fuente
4
$cat abs.sh
#!/bin/bash
echo "$(cd "$(dirname "$1")"; pwd -P)"

Algunas explicaciones:

  1. Este script obtiene una ruta relativa como argumento "$1"
  2. Luego obtenemos dirname parte de esa ruta (puede pasar dir o archivo a este script):dirname "$1"
  3. Entonces entramos cd "$(dirname "$1");en este directorio relativo
  4. pwd -Py obtener la ruta absoluta. La -Popción evitará los enlaces simbólicos
  5. Como paso final echolo

Luego ejecuta tu script:

abs.sh your_file.txt
Eugen Konkov
fuente
1

Pruebe nuestro nuevo producto de biblioteca Bash, realpath-lib, en GitHub, que le hemos proporcionado a la comunidad de forma gratuita y sin obstáculos. Es limpio, simple y bien documentado, por lo que es genial aprender de él. Tu puedes hacer:

get_realpath <absolute|relative|symlink|local file path>

Esta función es el núcleo de la biblioteca:

if [[ -f "$1" ]]
then
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

Es Bash 4+, no requiere ninguna dependencia y también proporciona get_dirname, get_filename, get_stemname y validate_path.

AsymLabs
fuente
0

El problema con la respuesta anterior viene con la entrada de archivos con "./" como "./my-file.txt"

Solución alternativa (de muchas):

    myfile="./somefile.txt"
    FOLDER="$(dirname $(readlink -f "${ARG}"))"
    echo ${FOLDER}
Mike Q
fuente
-1

He estado usando readlink -f funciona en linux

entonces

FULL_PATH=$(readlink -f filename)
DIR=$(dirname $FULL_PATH)

PWD=$(pwd)

cd $DIR

#<do more work>

cd $PWD
Mehul Rathod
fuente
5
no use nombres de variables en mayúsculas en Bash, algún día chocará con las variables ya existentes ... oh, y este día es ahora: ¿sabía que la variable PWDya existe en Bash y su valor es en realidad el directorio actual? Y use más citas también.
gniourf_gniourf