¿Cómo puedo obtener el directorio de origen de un script Bash desde el propio script?

¿Cómo obtengo la ruta del directorio en el que Asestar un golpe se encuentra el script, dentro ese guion?

Quiero usar un script Bash como lanzador para otra aplicación. Quiero cambiar el directorio de trabajo al donde se encuentra el script Bash, para poder operar en los archivos en ese directorio, así:

$ ./application

preguntado el 12 de septiembre de 08 a las 18:09

Ninguna de las soluciones actuales funciona si hay alguna nuevas líneas al final del nombre del directorio - Serán despojados por la sustitución de mando. Para evitar esto, puede agregar un carácter que no sea de nueva línea dentro de la sustitución del comando: DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)" - y elimínelo sin una sustitución de comando - DIR="${DIR%x}". -

@ jpmc26 Hay dos situaciones muy habituales: accidentes y sabotajes. Un guión no debería fallar de manera impredecible solo porque alguien, en algún lugar, hizo un mkdir $'\n'. -

Cualquiera que permita que la gente sabotee su sistema de esa manera no debería dejar en manos de bash la detección de tales problemas ... y mucho menos contratar personas capaces de cometer ese tipo de error. Nunca he visto, en los 25 años de uso de bash, que suceda este tipo de cosas en ningún lado ... es por eso que tenemos cosas como perl y prácticas como la comprobación de taint (probablemente me enfadaré por decir eso :) -

@ l0b0 Considere que necesitaría la misma protección en dirname, y que el directorio podría comenzar con un - (p.ej --help). DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}. ¿Quizás esto sea una exageración? -

Sugiero encarecidamente leer esto Preguntas frecuentes de Bash sobre el tema. -

30 Respuestas

#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

es un resumen útil que le dará el nombre completo del directorio del script sin importar desde dónde se llame.

Funcionará siempre que el último componente de la ruta utilizado para encontrar el script no sea un enlace simbólico (los enlaces de directorio están bien). Si también desea resolver los enlaces al script en sí, necesita una solución de varias líneas:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

Este último funcionará con cualquier combinación de alias, source, bash -c, enlaces simbólicos, etc.

Ten cuidado: si tu cd a un directorio diferente antes de ejecutar este fragmento, ¡el resultado puede ser incorrecto!

Además, cuidado con $CDPATH trampas, y los efectos secundarios de salida stderr si el usuario ha anulado de forma inteligente cd para redirigir la salida a stderr en su lugar (incluidas las secuencias de escape, como al llamar update_terminal_cwd >&2 en Mac). Añadiendo >/dev/null 2>&1 al final de tu cd El comando se encargará de ambas posibilidades.

Para comprender cómo funciona, intente ejecutar esta forma más detallada:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

E imprimirá algo como:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

Respondido el 30 de junio de 20 a las 19:06

Puede fusionar este enfoque con la respuesta del usuario 25866 para llegar a una solución que funcione con source <script> y bash <script>: DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)". - Moldeo Dan

A veces cd imprime algo en STDOUT! Por ejemplo, si tu $CDPATH tiene .. Para cubrir este caso, use DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )" - user716468

Esta respuesta aceptada no está bien, no funciona con enlaces simbólicos y es demasiado compleja. dirname $(readlink -f $0) es el comando correcto. Ver gist.github.com/tvlooy/cbfbdb111a4ebad8b93e para un caso de prueba - tvlooy

@tvlooy En mi opinión, su respuesta tampoco está exactamente bien tal como está, porque falla cuando hay un espacio en la ruta. A diferencia de un carácter de nueva línea, esto no es poco probable o incluso infrecuente. dirname "$(readlink -f "$0")" no agrega complejidad y es bastante más robusto para la mínima cantidad de problemas. - Adrián Gunter

@tvlooy su comentario no es compatible con macOS (o probablemente BSD en general), mientras que la respuesta aceptada sí lo es. readlink -f $0 da readlink: illegal option -- f. - Alejandro Ljungberg

Utiliza dirname "$0":

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

Usar pwd por sí solo no funcionará si no está ejecutando el script desde el directorio en el que está contenido.

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

Respondido el 20 de enero de 21 a las 02:01

Para una portabilidad más allá de bash, es posible que $ 0 no siempre sea suficiente. Es posible que deba sustituir "type -p $ 0" para que esto funcione si el comando se encontró en la ruta. - Darron

@Darron: solo puedes usar type -p si el script es ejecutable. Esto también puede abrir un agujero sutil si el script se ejecuta usando bash test2.sh y hay otro script con el mismo nombre ejecutable en otro lugar. - D.Shawley

@Darron: pero ya que la pregunta está etiquetada bash y la línea hash-bang menciona explícitamente /bin/bash Yo diría que es bastante seguro depender de bashismos. - Joaquín Sauer

+1, pero el problema con el uso dirname $0 es que si el directorio es el directorio actual, obtendrá .. Eso está bien a menos que vaya a cambiar de directorio en el script y espere usar la ruta que obtuvo. dirname $0 como si fuera absoluto. Para obtener la ruta absoluta: pushd `dirname $0` > /dev/null, SCRIPTPATH=`pwd`, popd > /dev/null: pastie.org/1489386 (Pero seguramente ¿Hay una mejor manera de expandir ese camino?) - TJ Crowder

@TJ Crowder, no estoy seguro dirname $0 es un problema si lo asigna a una variable y luego lo usa para iniciar un script como $dir/script.sh; Me imagino que este es el caso de uso para este tipo de cosas el 90% del tiempo. ./script.sh funcionaría bien. - mate b

La dirname El comando es el más básico, simplemente analiza la ruta hasta el nombre del archivo fuera de la $0 (nombre del script) variable:

dirname "$0"

Pero como mate b señaló, la ruta devuelta es diferente dependiendo de cómo se llame al script. pwd no hace el trabajo porque eso solo le dice cuál es el directorio actual, no en qué directorio reside el script. Además, si se ejecuta un enlace simbólico a un script, obtendrá una ruta (probablemente relativa) a donde reside el enlace, no el script real.

Algunos otros han mencionado el readlink comando, pero en su forma más simple, puede usar:

dirname "$(readlink -f "$0")"

readlink resolverá la ruta del script a una ruta absoluta desde la raíz del sistema de archivos. Por lo tanto, cualquier ruta que contenga puntos simples o dobles, tildes y / o enlaces simbólicos se resolverá en una ruta completa.

Aquí hay un guión que demuestra cada uno de estos whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

Ejecutando este script en mi directorio de inicio, usando una ruta relativa:

>>>$ ./whatdir.sh
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

Nuevamente, pero usando la ruta completa al script:

>>>$ /Users/phatblat/whatdir.sh
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Ahora cambiando de directorio:

>>>$ cd /tmp
>>>$ ~/whatdir.sh
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Y finalmente usando un enlace simbólico para ejecutar el script:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

Respondido el 20 de enero de 21 a las 03:01

readlink no estará disponible en algunas plataformas en la instalación predeterminada. Trate de evitar usarlo si puede: TL

tenga cuidado de citar todo para evitar problemas de espacios en blanco: export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" - Catskul

En OSX Yosemite 10.10.1 -f no se reconoce como una opción para readlink. Utilizando stat -f en cambio, hace el trabajo. Gracias - cucu8

En OSX, hay greadlink, que es básicamente el readlink todos estamos familiarizados. Aquí hay una versión independiente de la plataforma: dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}` - Robert

Buena llamada, @robert. FYI, greadlink se puede instalar fácilmente a través de homebrew: brew install coreutils - gordo

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`;
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

Funciona para todas las versiones, incluidas

  • cuando se llama a través de un enlace suave de profundidad múltiple,
  • cuando el archivo es
  • cuando el script es llamado por comando "source"alias . operador (punto).
  • cuando arg $0 se modifica de la persona que llama.
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

Alternativamente, si el script Bash en sí es un enlace simbólico relativoquieres para seguirlo y devolver la ruta completa del script vinculado:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATH se da en la ruta completa, no importa cómo se llame.

Solo asegúrese de ubicar esto al comienzo del script.

Respondido el 20 de enero de 21 a las 02:01

¡Lindo! Podría acortarse reemplazando "pushd [...] popd / dev / null" por SCRIPT_PATH =readlink -f $(dirname "${VIRTUAL_ENV}"); - e-satis

Y en lugar de usar pushd ...; ¿No sería mejor usar $ (cd dirname "${SCRIPT_PATH}" && pwd)? ¡Pero de todos modos gran guión! - óvulos

Es peligroso que un guión cd fuera de su directorio actual con la esperanza de cdvolver de nuevo más tarde: Es posible que el script no tenga permiso para volver a cambiar el directorio al directorio que estaba actual cuando se invocó. (Lo mismo ocurre con pushd / popd) - Adrián Pronk

readlink -f es específico de GNU. BSD readlink no tiene esa opción. - Kara Brightwell

¿Qué pasa con todas las subcapas innecesarias? ([ ... ]) es menos eficiente que [ ... ], y no se aprovecha el aislamiento ofrecido a cambio de ese impacto de rendimiento aquí. - Carlos Duffy

Puede usar el $BASH_SOURCE:

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Tenga en cuenta que necesita usar #!/bin/bash y no #!/bin/sh ya que es una extensión de Bash.

Respondido el 18 de enero de 20 a las 18:01

Cuando lo hago ./foo/script, entonces $(dirname $BASH_SOURCE) is ./foo. - a

@Till, en este caso podemos usar realpath comando para obtener la ruta completa de ./foo/script. Entonces dirname $(realpath ./foo/script) le dará la ruta de la secuencia de comandos. - purushothaman poovai

Respuesta corta:

`dirname $0`

o (preferiblemente):

$(dirname "$0")

Respondido 19 Abr '17, 07:04

No funcionará si obtiene el script. "fuente mi / script.sh" - Arunprasad Rajkumar

Utilizo esto todo el tiempo en mis scripts bash que automatizan cosas y, a menudo, invocan otros scripts en el mismo directorio. Nunca usaría source en estos y cd $(dirname $0) es fácil de recordar. - kww

@vidstige: ${BASH_SOURCE[0]} en lugar de $0 trabajará con source my/script.sh - timoteo jones

@TimothyJones que fallará el 100% del tiempo si se obtiene de cualquier otro shell que no sea bash. ${BASH_SOURCE[0]} no es satisfactorio en absoluto. ${BASH_SOURCE:-0} es mucho mejor. - Mathieu CAROFF

Esto debería hacerlo:

DIR="$(dirname "$(readlink -f "$0")")"

Esto funciona con enlaces simbólicos y espacios en la ruta.

Consulte las páginas de manual para dirname y readlink.

Desde la pista de comentarios, parece que no funciona con Mac OS. No tengo idea de por qué es eso. ¿Alguna sugerencia?

respondido 19 mar '20, 20:03

con su solución, invocando el script como ./script.sh enseñe . en lugar de la ruta completa del directorio - Bruno Negro Zica

No hay opción -f para readlink en MacOS. Usar stat en lugar de. Pero aun así, se nota . si está en 'este' directorio. - Denis la amenaza

Necesitas instalar coreutils de Homebrew y uso greadlink para obtener el -f opción en MacOS porque es * BSD oculto y no Linux. - dragon788

Debe agregar comillas dobles alrededor de todo el lado derecho: DIR="$(dirname "$(readlink -f "$0")")" - hagello

Aquí hay un guión fácil de recordar:

DIR="$(dirname "${BASH_SOURCE[0]}")"  # Get the directory name
DIR="$(realpath "${DIR}")"    # Resolve its full path if need be

Respondido el 20 de enero de 21 a las 04:01

O, de forma más oscura, en una línea: DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")") - agc

¿Por qué no es esta la respuesta aceptada? ¿Hay alguna diferencia al usar realpath de resolver "manualmente" con un bucle de readlink? Incluso el readlink la página man dice Note realpath(1) is the preferred command to use for canonicalization functionality. - User9123

Y por cierto, ¿no deberíamos aplicar? realpath antes dirname, ¿no después de? Si el archivo de script en sí es un enlace simbólico ... daría algo como DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")". Realmente muy cerca de la respuesta propuesta por Simon. - User9123

@ User9123 Creo que el aceptar es intentar ser compatible con todos los shell / distro populares. Además, dependiendo de lo que intente hacer, en la mayoría de los casos la gente quiere obtener el directorio donde se encuentra el enlace simbólico en lugar del directorio de la fuente real. - Wang

La única razón es que faltan coreutils en mac. estoy usando SCRIPT=$(realpath "${BASH_SOURCE[0]}") + DIR=$(dirname "$SCRIPT"). - puchu

pwd se puede utilizar para encontrar el directorio de trabajo actual, y dirname para encontrar el directorio de un archivo en particular (comando que se ejecutó, es $0, asi que dirname $0 debería darle el directorio del script actual).

Sin embargo, dirname da con precisión la parte del directorio del nombre del archivo, que probablemente será relativo al directorio de trabajo actual. Si su secuencia de comandos necesita cambiar de directorio por alguna razón, entonces la salida de dirname pierde sentido.

Sugiero lo siguiente:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

De esta manera, obtiene un directorio absoluto, en lugar de relativo.

Dado que el script se ejecutará en una instancia de Bash separada, no es necesario restaurar el directorio de trabajo posteriormente, pero si desea volver a cambiar en su script por alguna razón, puede asignar fácilmente el valor de pwd a una variable antes de cambiar de directorio, para uso futuro.

Aunque solo

cd `dirname $0`

resuelve el escenario específico en la pregunta, encuentro que tener el camino absoluto a más útil en general.

Respondido el 20 de enero de 21 a las 02:01

Puede hacerlo todo en una línea como esta: DIRECTORIO = $ (cd dirname $0 && pwd) - acónito

Esto no funciona si el script obtiene otro script y desea saber el nombre de este último. - reinierpost

No creo que esto sea tan fácil como otros lo han hecho. pwd no funciona, ya que el directorio actual no es necesariamente el directorio con el script. $0 tampoco siempre tiene la información. Considere las siguientes tres formas de invocar un script:

./script

/usr/bin/script

script

De la primera y tercera forma $0 no tiene la información de ruta completa. En el segundo y tercero, pwd No funciona. La única forma de obtener el directorio de la tercera forma sería recorrer la ruta y encontrar el archivo con la coincidencia correcta. Básicamente, el código tendría que rehacer lo que hace el sistema operativo.

Una forma de hacer lo que está pidiendo sería simplemente codificar los datos en el /usr/share directorio y referenciarlo por su ruta completa. Los datos no deben estar en el /usr/bin directorio de todos modos, por lo que probablemente esto sea lo que se debe hacer.

Respondido el 18 de enero de 20 a las 18:01

Si tiene la intención de refutar su comentario, DEMUESTRE que un script PUEDE acceder a donde está almacenado con un ejemplo de código. - ricardo duerr

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )

contestado el 30 de mayo de 13 a las 12:05

Esta es mucho más corta que la respuesta elegida. Y parece funcionar igual de bien. Esto merece 1000 votos solo para que la gente no lo pase por alto. - Patrick

Como muchas de las respuestas anteriores explican en detalle, ninguna de las dos $0 ni pwd están garantizados para tener la información correcta, dependiendo de cómo se invoque el script. - IMSoP

$(dirname "$(readlink -f "$BASH_SOURCE")")

contestado el 13 de mayo de 18 a las 05:05

Yo prefiero, $BASH_SOURCE encima $0 , porque es explícito incluso para lectores que no conocen bien bash. $(dirname -- "$(readlink -f -- "$BASH_SOURCE")") - blobmaster

Esto obtiene el directorio de trabajo actual en Mac OS X v10.6.6 (Leopardo de nieve):

DIR=$(cd "$(dirname "$0")"; pwd)

Respondido el 20 de enero de 21 a las 03:01

Esto es específico de Linux, pero podría usar:

SELF=$(readlink /proc/$$/fd/255)

Respondido el 22 de diciembre de 13 a las 00:12

También es específico de bash, pero ¿quizás el comportamiento de bash ha cambiado? /proc/fd/$$/255 parece apuntar al tty, no a un directorio. Por ejemplo, en mi shell de inicio de sesión actual, los descriptores de archivo 0, 1, 2 y 255 se refieren a /dev/pts/4. En cualquier caso, el manual de bash no menciona fd 255, por lo que probablemente no sea prudente depender de este comportamiento. \ - Keith Thompson

Shell interactivo! = Script. De todas formas realpath ${BASH_SOURCE[0]}; parece ser la mejor manera de hacerlo. - Steve Baker

Aquí hay un resumen compatible con POSIX:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

Respondido 15 Abr '13, 08:04

Tuve éxito con esto cuando ejecuté un script por sí mismo o usando sudo, pero no cuando llamé a source ./script.sh - Michael R

Y falla cuando cd está configurado para imprimir el nuevo nombre de ruta. - Aarón Digulla

La forma más corta y elegante de hacer esto es:

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

Esto funcionaría en todas las plataformas y es súper limpio.

Se pueden encontrar más detalles en "¿En qué directorio está ese script bash?".

Respondido el 18 de enero de 20 a las 18:01

gran solución limpia, pero esto no funcionará si el archivo tiene un enlace simbólico. - enrutador

Probé todos estos y ninguno funcionó. Uno estaba muy cerca, pero tenía un bichito que lo rompió mal; se olvidaron de poner el camino entre comillas.

Además, mucha gente asume que está ejecutando el script desde un shell, por lo que se olvidan cuando abre un nuevo script, el predeterminado es su hogar.

Pruebe este directorio para ver el tamaño:

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

Esto lo hace bien independientemente de cómo o dónde lo ejecute:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

Entonces, para que sea realmente útil, aquí se explica cómo cambiar al directorio del script en ejecución:

cd "`dirname "$0"`"

Respondido el 20 de enero de 21 a las 03:01

No funciona si el script proviene de otro script. - reinierpost

Esto no funciona si la última parte de $ 0 es un enlace simbólico que apunta a una entrada de otro directorio (ln -s ../bin64/foo /usr/bin/foo). - hagello

Esta es la forma sencilla y correcta:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

Explicación:

  • ${BASH_SOURCE[0]} - la ruta completa al guión. El valor de esto será correcto incluso cuando se obtenga el script, p. Ej. source <(echo 'echo $0') huellas dactilares golpear, mientras lo reemplaza con ${BASH_SOURCE[0]} imprimirá la ruta completa del script. (Por supuesto, esto asume que está bien tomando una dependencia en Bash).

  • readlink -f - Resuelve recursivamente cualquier enlace simbólico en la ruta especificada. Esta es una extensión GNU y no está disponible en (por ejemplo) sistemas BSD. Si está ejecutando una Mac, puede usar Homebrew para instalar GNU coreutils y suplantar esto con greadlink -f.

  • Y por supuesto dirname obtiene el directorio padre de la ruta.

Respondido 20 Feb 16, 07:02

greadlink -f desafortunadamente no funciona de manera efectiva cuando sourceing el script en Mac :( - gabe kopley

#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

Respondido el 13 de Septiembre de 08 a las 02:09

No lo he probado en diferentes sistemas. ¡Pero esta solución es la que funciona de inmediato al menos en Ubuntu, para mí! - Natus dibujó

Esta es una ligera revisión de la solución e-satis y 3bcdnlklvc04a señalada en su respuesta:

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}

Esto aún debería funcionar en todos los casos que enumeraron.

Esto evitará popd después de un fallido pushd. Gracias a konsolebox.

Respondido el 20 de enero de 21 a las 03:01

Esto funciona perfectamente para obtener el nombre de directorio "real", en lugar de solo el nombre de un enlace simbólico. ¡Gracias! - Jay Taylor

Mejor SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; } - konsolebox

@konsolebox, ¿contra qué intentas defenderte? Generalmente soy un fanático de los condicionales lógicos en línea, pero ¿cuál fue el error específico que estaba viendo en el pushd? Prefiero encontrar una manera de manejarlo directamente en lugar de devolver un SCRIPT_DIR vacío. - fuwjax

@Fuwjax Práctica natural para evitar hacer popd en casos (incluso cuando son raros) donde pushd falla. Y por si acaso pushd falla, ¿cuál crees que debería ser el valor de SCRIPT_DIR? La acción puede variar dependiendo de lo que pueda parecer lógico o de lo que prefiera un usuario, pero ciertamente, al hacer popd Está Mal. - konsolebox

Todos aquellos pushd popd Los peligros se pueden evitar simplemente dejándolos caer y usando cd + pwd incluido en una sustitución de comando en su lugar. SCRIPT_DIR=$(...) - Amit Naidu

Usaría algo como esto:

# Retrieve the full pathname of the called script
scriptPath=$(which $0)

# Check whether the path is a link or not
if [ -L $scriptPath ]; then

    # It is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # Otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

Respondido el 20 de enero de 21 a las 04:01

¡Este es el verdadero! Funciona con simple sh ¡también! Problema con simple dirname "$0" soluciones basadas en: si el script está en el $PATH y se invoca sin camino, darán un resultado incorrecto. - No en lista

@Notinlist No es así. Si el script se encuentra a través del PATH, $0 contendrá el nombre de archivo absoluto. Si el script se invoca con un nombre de archivo relativo o absoluto que contiene un /, $0 contendrá eso. - neil mayhew

Para sistemas que tienen GNU coreutils readlink (por ejemplo, Linux):

$(readlink -f "$(dirname "$0")")

No hay necesidad de usar BASH_SOURCE cuando $0 contiene el nombre del archivo de secuencia de comandos.

Respondido el 20 de enero de 21 a las 04:01

a menos que el guión se haya obtenido con. o 'fuente', en cuyo caso seguirá siendo el script que lo haya originado, o, si es desde la línea de comando, '-bash' (tty login) o 'bash' (invocado a través de 'bash -l') o '/ bin / bash '(invocado como un shell interactivo sin inicio de sesión) - osirisgothra

Agregué un segundo par de comillas alrededor dirname llamada. Necesario si la ruta del directorio contiene espacios. - user1338062

$_ Vale la pena mencionar como alternativa a $0. Si está ejecutando un script desde Bash, la respuesta aceptada se puede abreviar a:

DIR="$( dirname "$_" )"

Tenga en cuenta que esta debe ser la primera declaración de su secuencia de comandos.

Respondido el 18 de enero de 20 a las 18:01

Se rompe si tu source or . la secuencia de comandos. En esas situaciones, $_ contendría el último parámetro del último comando que ejecutó antes del .. $BASH_SOURCE funciona todo el tiempo. - claqué

Es Perl-¡como! ¿Una coincidencia? - Pedro Mortensen

Estas son formas breves de obtener información sobre el script:

Carpetas y archivos:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

Usando estos comandos:

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

Y obtuve esta salida:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

Ver también: https://pastebin.com/J8KjxrPF

contestado el 15 de mayo de 20 a las 01:05

Creo que mi respuesta está bien porque es difícil encontrar una edición funcional simple. Aquí puede tomar el código que desee, por ejemplo, cd + pwd, dirname + realpath o dirname + readlink. No estoy seguro de que todas las partes existan antes y la mayoría de las respuestas son complejas y sobrecargadas. Aquí puede extraer el código que le gusta usar. Al menos, no lo elimine ya que lo necesito en el futuro: D - User8461

Esto funciona en Bash 3.2:

path="$( dirname "$( which "$0" )" )"

Si usted tiene una ~/bin directorio en tu $PATH, tienes A dentro de este directorio. Obtiene el guión ~/bin/lib/B. Sabe dónde está el guión incluido en relación con el original, en el lib subdirectorio, pero no donde es relativo al directorio actual del usuario.

Esto se resuelve con lo siguiente (dentro A):

source "$( dirname "$( which "$0" )" )/lib/B"

No importa dónde esté el usuario o cómo llame al script. Esto siempre funcionará.

Respondido el 20 de enero de 21 a las 02:01

El punto en which es muy discutible. type, hash, y otras incorporaciones hacen lo mismo mejor en bash. which es un poco más portátil, aunque en realidad no es lo mismo which utilizado en otras conchas como tcsh, que lo tiene incorporado. - Reincorporar a Monica por favor

"Siempre"? Para nada. which al ser una herramienta externa, no tiene ninguna razón para creer que se comporta de manera idéntica al shell padre. - Carlos Duffy

He comparado muchas de las respuestas dadas y he encontrado algunas soluciones más compactas. Estos parecen manejar todos los casos extremos locos que surgen de su combinación favorita de:

  • Rutas absolutas o rutas relativas
  • Enlaces suaves de archivos y directorios
  • Invocación como script, bash script, bash -c script, source script o el . script
  • Espacios, pestañas, nuevas líneas, Unicode, etc.en directorios y / o nombre de archivo
  • Nombres de archivo que comienzan con un guion

Si está ejecutando desde Linux, parece que al usar el proc handle es la mejor solución para localizar la fuente completamente resuelta del script que se está ejecutando actualmente (en una sesión interactiva, el enlace apunta al /dev/pts/X):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

Esto tiene un poco de fealdad, pero la solución es compacta y fácil de entender. No estamos usando solo primitivas de bash, pero estoy de acuerdo con eso porque readlink simplifica considerablemente la tarea. La echo X agrega un X al final de la cadena de variables para que no se coma ningún espacio en blanco final en el nombre del archivo, y la sustitución del parámetro ${VAR%X} al final de la línea se deshace del X. Porque readlink agrega una nueva línea propia (que normalmente se comería en la sustitución de comandos si no fuera por nuestro truco anterior), también tenemos que deshacernos de eso. Esto se logra más fácilmente usando el $'' esquema de comillas, que nos permite usar secuencias de escape como \n para representar nuevas líneas (esta es también la forma en que puede crear fácilmente directorios y archivos con nombres engañosos).

Lo anterior debería cubrir sus necesidades para localizar el script que se está ejecutando actualmente en Linux, pero si no tiene el proc sistema de archivos a su disposición, o si está tratando de localizar la ruta completamente resuelta de algún otro archivo, entonces tal vez encuentre útil el siguiente código. Es solo una pequeña modificación de la frase anterior. Si está jugando con directorios / nombres de archivo extraños, verifique la salida con ambos ls y readlink es informativo, ya que ls generará rutas "simplificadas", sustituyendo ? para cosas como nuevas líneas.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

Respondido el 20 de enero de 21 a las 04:01

yo obtengo /dev/pts/30 con bash en Ubuntu 14.10 Desktop. - Dan Dascalescu

@DanDascalescu ¿Usando el one-liner? ¿O el fragmento de código completo en la parte inferior? ¿Y le estabas dando algún nombre de ruta complicado? - Billyjmc

La una línea más otra línea para echo $resolved, Lo guardé como d, chmod +x d, ./d. - Dan Dascalescu

@DanDascalescu La primera línea de su secuencia de comandos debe ser #!/bin/bash - Billyjmc

Intente usar:

real=$(realpath $(dirname $0))

Respondido 06 Feb 12, 11:02

Todo lo que quiero saber es, ¿por qué esta forma no es buena? No me pareció nada malo y correcto. ¿Alguien podría explicar por qué se votó negativamente? - Shou ya

realpath no es una utilidad estándar. - Steve Bennett

En Linux, realpath es una utilidad estándar (parte del paquete GNU coreutils), pero no es una función integrada de bash (es decir, una función proporcionada por el propio bash). Si está ejecutando Linux, este método probablemente funcionará, aunque lo sustituiría por $0 para ${BASH_SOURCE[0]} para que este método funcione en cualquier lugar, incluso en una función. - doug richardson

El orden de las operaciones en esta respuesta es incorrecto. Necesitas en el primer resolver el enlace simbólico, luego do dirname porque la última parte de $0 puede ser un enlace simbólico que apunta a un archivo que no está en el mismo directorio que el enlace simbólico. La solución descrita en esta respuesta solo obtiene la ruta del directorio donde se almacenó el enlace simbólico, no el directorio del objetivo. Además, a esta solución le faltan cotizaciones. No funcionará si la ruta contiene caracteres especiales. - hagello

dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" - Kostiantyn Ponomarenko

Creo que tengo este. Llego tarde a la fiesta, pero creo que algunos apreciarán estar aquí si se encuentran con este hilo. Los comentarios deben explicar:

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "$@"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "$@" | cut -c1)" = '/' ]; then
   printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "$@"
fi

Respondido el 18 de enero de 20 a las 18:01

Cómo obtener el ruta de archivo completa, directorio completoy nombre de archivo base de cualquier script que se ejecute por sí mismo

En muchos casos, todo lo que necesita adquirir es la ruta completa al script que acaba de llamar. Esto se puede lograr fácilmente usando realpath. Tenga en cuenta que realpath es parte de Coreutils de GNU. Si aún no lo tiene instalado (viene predeterminado en Ubuntu), puede instalarlo con sudo apt update && sudo apt install coreutils.

get_script_path.sh:

#!/bin/bash

FULL_PATH_TO_SCRIPT="$(realpath "$0")"

# You can then also get the full path to the directory, and the base
# filename, like this:
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"

# Now print it all out
echo "FULL_PATH_TO_SCRIPT = \"$FULL_PATH_TO_SCRIPT\""
echo "SCRIPT_DIRECTORY    = \"$SCRIPT_DIRECTORY\""
echo "SCRIPT_FILENAME     = \"$SCRIPT_FILENAME\""

Ejemplo de salida:

~/GS/dev/eRCaGuy_hello_world/bash$ ./get_script_path.sh 
FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
SCRIPT_DIRECTORY    = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
SCRIPT_FILENAME     = "get_script_path.sh"

Tenga en cuenta que realpath también recorre con éxito enlaces simbólicos para determinar y señalar sus objetivos en lugar de apuntar al enlace simbólico.

El código anterior ahora es parte de mi eRCaGuy_hola_mundo repositorio en este archivo aquí: bash / get_script_path.sh.

Referencias:

  1. Cómo recuperar la ruta absoluta dada relativa

Respondido el 28 de diciembre de 20 a las 05:12

Pruebe la siguiente solución de compatibilidad cruzada:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

Como los comandos como realpath or readlink podría no estar disponible (según el sistema operativo).

Nota: en Bash, se recomienda usar ${BASH_SOURCE[0]} en lugar de $0, de lo contrario, la ruta puede romperse al obtener el archivo (source/.).

Alternativamente, puede probar la siguiente función en Bash:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Esta función toma un argumento. Si el argumento ya tiene una ruta absoluta, imprímalo como está, de lo contrario imprima $PWD variable + argumento de nombre de archivo (sin ./ prefijo).

Relacionado:

Respondido el 20 de enero de 21 a las 04:01

Explique más sobre la función realpath. - Chris

@Chris realpath la función toma 1 argumento. Si el argumento ya tiene una ruta absoluta, imprímalo como está, de lo contrario imprima $PWD + nombre de archivo (sin ./ prefijo). - Kenorb

Su solución de compatibilidad cruzada no funciona cuando el script tiene un enlace simbólico. - Jakub Jirutka

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.