¿Cómo puedo obtener el directorio de origen de un script Bash desde el propio script?
Frecuentes
Visto 1,894 equipos
5280
¿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
30 Respuestas
6991
#!/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
964
Utilice la herramienta 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
547
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
186
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 relativo te lleve quieres 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 cd
volver 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
114
Puedes usar $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
112
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
83
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
78
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")
. - abajo
66
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
39
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
35
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
34
$(dirname "$(readlink -f "$BASH_SOURCE")")
contestado el 13 de mayo de 18 a las 05:05
Yo prefiero, $BASH_SOURCE
Más de $0
, porque es explícito incluso para lectores que no conocen bien bash. $(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
- blobmaster
34
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
27
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
23
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
19
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
18
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
17
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 GNUcoreutils
y suplantar esto congreadlink -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 source
ing el script en Mac :( - gabe kopley
16
#!/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ó
16
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
16
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
16
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
13
$_
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
11
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
11
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
11
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. 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
10
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
en ${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 la primera 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
9
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
9
Cómo obtener el ruta de archivo completa, directorio completo y 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:
Respondido el 28 de diciembre de 20 a las 05:12
9
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 bash directory or haz tu propia pregunta.
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}"
. - l0b0@ 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'
. - l0b0Cualquiera 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 :) - osirisgothra
@ 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? - Score_UnderSugiero encarecidamente leer esto Preguntas frecuentes de Bash sobre el tema. - Rany Albeg Wein