¿Cómo obtengo la ruta del directorio en el que se encuentra un script Bash , dentro de ese script?
Quiero usar un script Bash como lanzador para otra aplicación. Quiero cambiar el directorio de trabajo al que está ubicado el script Bash, para poder operar en los archivos de ese directorio, así:
$ ./application
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"
- y eliminarlo sin una sustitución de comando -DIR="${DIR%x}"
.mkdir $'\n'
.dirname
y que el directorio podría comenzar con un-
(por ejemplo--help
).DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}
. Tal vez esto es exagerado?Respuestas:
es una línea útil que le dará el nombre completo del directorio de la secuencia de comandos sin importar de dónde se llame.
Funcionará siempre que el último componente de la ruta utilizada para encontrar el script no sea un enlace simbólico (los enlaces de directorio están bien). Si también desea resolver cualquier enlace al script en sí, necesita una solución de varias líneas:
Este último funciona con cualquier combinación de alias,
source
,bash -c
, enlaces simbólicos, etc.Cuidado: si va
cd
a un directorio diferente antes de ejecutar este fragmento, ¡el resultado puede ser incorrecto!Además, tenga cuidado con las
$CDPATH
trampas y los efectos secundarios de salida de stderr si el usuario ha anulado de manera inteligente el cd para redirigir la salida a stderr en su lugar (incluidas las secuencias de escape, como cuando se llamaupdate_terminal_cwd >&2
en Mac). Agregar>/dev/null 2>&1
al final de sucd
comando se encargará de ambas posibilidades.Para entender cómo funciona, intente ejecutar esta forma más detallada:
E imprimirá algo como:
fuente
source <script>
ybash <script>
:DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
.cd
imprime algo en STDOUT! Por ejemplo, si su$CDPATH
cuenta.
. Para cubrir este caso, useDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
dirname $(readlink -f $0)
es el comando correcto Ver gist.github.com/tvlooy/cbfbdb111a4ebad8b93e para un caso de pruebadirname "$(readlink -f "$0")"
no agrega complejidad y es una medida justa más robusta para la mínima cantidad de problemas.readlink -f $0
dareadlink: illegal option -- f
.Uso
dirname "$0"
:usar
pwd
solo no funcionará si no está ejecutando el script desde el directorio en el que está contenido.fuente
type -p
si el script es ejecutable. Esto también puede abrir un agujero sutil si el script se ejecuta usandobash test2.sh
y hay otro script con el mismo nombre ejecutable en otro lugar.bash
y la línea hash-bang menciona explícitamente/bin/bash
, diría que es bastante seguro depender de los bashismos.dirname $0
es que si el directorio es el directorio actual, obtendrá.
. Eso está bien a menos que vayas a cambiar los directorios en el script y esperes usar la ruta que obtuvistedirname $0
como si fuera absoluta. Para obtener la ruta absoluta:pushd `dirname $0` > /dev/null
,SCRIPTPATH=`pwd`
,popd > /dev/null
: pastie.org/1489386 (Pero sin duda hay una mejor manera de ampliar ese camino?)dirname $0
haya 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 bienEl
dirname
comando es el más básico, simplemente analiza la ruta hasta el nombre de archivo fuera de la$0
variable (nombre del script):Pero, como señaló Matt B , el camino devuelto es diferente dependiendo de cómo se llame el 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: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 script que demuestra cada uno de estos
whatdir.sh
:Ejecutando este script en mi directorio de inicio, usando una ruta relativa:
De nuevo, pero usando la ruta completa al script:
Ahora cambiando directorios:
Y finalmente usando un enlace simbólico para ejecutar el script:
fuente
readlink
no estará disponible en alguna plataforma en la instalación predeterminada. Intenta evitar usarlo si puedesexport SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
-f
no se reconoce como una opción parareadlink
. Usar en sustat -f
lugar hace el trabajo. Graciasgreadlink
, que es básicamente lareadlink
que todos conocemos. Aquí hay una versión independiente de la plataforma:dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
greadlink
se puede instalar fácilmente a través de homebrew:brew install coreutils
Funciona para todas las versiones, incluidas
source
".
operador aka (punto).$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 , desea seguirlo y devolver la ruta completa del script vinculado:
SCRIPT_PATH
se da en ruta completa, no importa cómo se llame.Solo asegúrate de localizar esto al comienzo del script.
Este comentario y código Copyleft, licencia seleccionable bajo la GPL2.0 o posterior o CC-SA 3.0 (CreativeCommons Share Alike) o posterior. (c) 2008. Todos los derechos reservados. Sin garantía de ningún tipo. Usted ha sido advertido.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656
fuente
readlink -f $(dirname "${VIRTUAL_ENV}")
;dirname "${SCRIPT_PATH}"
&& pwd)? Pero de todos modos gran guión!cd
salga de su directorio actual con la esperanza decd
volver a aparecer más tarde: la secuencia de comandos puede no tener permiso para volver a cambiar el directorio actual al que se invocó. (Lo mismo ocurre con pushd / popd)readlink -f
es específico de GNU. BSDreadlink
no tiene esa opción.([ ... ])
es menos eficiente que[ ... ]
, y no se aprovecha el aislamiento ofrecido a cambio de ese rendimiento alcanzado aquí.Respuesta corta:
o ( preferiblemente ):
fuente
source
ycd $(dirname $0)
es fácil de recordar.${BASH_SOURCE[0]}
lugar de$0
trabajar consource my/script.sh
${BASH_SOURCE[0]}
no es satisfactorio en absoluto${BASH_SOURCE:-0}
es mucho mejor.Puedes usar
$BASH_SOURCE
:Tenga en cuenta que debe usar
#!/bin/bash
y no,#!/bin/sh
ya que es una extensión Bash.fuente
./foo/script
, entonces lo$(dirname $BASH_SOURCE)
es./foo
.realpath
comando para obtener la ruta completa de ./foo/script. Asídirname $(realpath ./foo/script)
dará el camino del guión.Esto debería hacerlo:
Esto funciona con enlaces simbólicos y espacios en la ruta.
Vea las páginas de manual para
dirname
yreadlink
.De la pista de comentarios parece que no funciona con Mac OS. No tengo idea de por qué es eso. ¿Alguna sugerencia?
fuente
./script.sh
muestra en.
lugar de la ruta completa del directoriostat
lugar. Pero aún así, muestra.
si estás en 'este' directorio.coreutils
desde Homebrew y usarlogreadlink
para obtener la-f
opción en MacOS porque es * BSD debajo de las cubiertas y no Linux.DIR="$(dirname "$(readlink -f "$0")")"
pwd
se puede usar para encontrar el directorio de trabajo actual ydirname
para encontrar el directorio de un archivo en particular (el comando que se ejecutó es$0
, pordirname $0
lo tanto, debería darle el directorio del script actual).Sin embargo,
dirname
proporciona con precisión la parte del directorio del nombre de archivo, que probablemente será relativa al directorio de trabajo actual. Si su script necesita cambiar de directorio por alguna razón, entonces la salida de no tienedirname
sentido.Sugiero lo siguiente:
De esta manera, obtienes un directorio absoluto, más que relativo.
Dado que el script se ejecutará en una instancia de bash separada, no es necesario restaurar el directorio de trabajo después, pero si desea volver a cambiar su script por alguna razón, puede asignar fácilmente el valor de
pwd
una variable antes de cambiar directorio, para uso futuro.Aunque solo
resuelve el escenario específico en la pregunta, creo que tener el camino absoluto a más más generalmente útil.
fuente
dirname $0
&& pwd)Estoy cansado de ir a esta página una y otra vez para copiar y pegar la línea en la respuesta aceptada. El problema con eso es que no es fácil de entender y recordar.
Aquí hay un script fácil de recordar:
fuente
DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
realpath
de resolver "manualmente" con un bucle dereadlink
? Incluso lareadlink
página del manual diceNote realpath(1) is the preferred command to use for canonicalization functionality.
realpath
antesdirname
, no después? Si el archivo de script en sí es un enlace simbólico ... Daría algo asíDIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
. En realidad muy cerca de la respuesta propuesta por Simon.No creo que esto sea tan fácil como lo han hecho otros.
pwd
no funciona, ya que el directorio actual no es necesariamente el directorio con el script.$0
no siempre tiene la información tampoco. Considere las siguientes tres formas de invocar un script:En 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 ejecutar 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 hacer referencia a ellos por su ruta completa. Los datos no deben estar en el/usr/bin
directorio de todos modos, por lo que probablemente sea lo que hay que hacer.fuente
fuente
$0
nipwd
se garantiza que tengan la información correcta, dependiendo de cómo se invoque el script.fuente
$BASH_SOURCE
más$0
, porque es explícito incluso para lectores que no están bien versados en bash.$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
Esto obtiene el directorio de trabajo actual en Mac OS X 10.6.6:
fuente
Esto es específico de Linux, pero podría usar:
fuente
/proc/fd/$$/255
parece apuntar a tty, no a un directorio. Por ejemplo, en mi shell de inicio de sesión actual, todos los descriptores de archivo 0, 1, 2 y 255 hacen referencia/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. \realpath ${BASH_SOURCE[0]};
, parece ser el mejor camino a seguir.Aquí hay un one-liner compatible con POSIX:
fuente
cd
se configura para imprimir el nuevo nombre de ruta.Intenté todo esto y ninguno funcionó. Uno estaba muy cerca pero tenía un pequeño error que lo rompió gravemente; se olvidaron de envolver el camino entre comillas.
Además, muchas personas suponen que está ejecutando el script desde un shell, por lo que se olvidan que cuando abre un nuevo script, el valor predeterminado es su hogar.
Pruebe este directorio para ver el tamaño:
Esto lo hace bien independientemente de cómo o dónde lo ejecute:
Entonces, para que sea realmente útil, aquí le mostramos cómo cambiar al directorio del script en ejecución:
fuente
ln -s ../bin64/foo /usr/bin/foo
).Aquí está la forma simple y correcta:
Explicación:
${BASH_SOURCE[0]}
- la ruta completa al guión. El valor de esto será correcto incluso cuando se está obteniendo el script, por ejemplo,source <(echo 'echo $0')
imprime bash , mientras que si lo reemplaza${BASH_SOURCE[0]}
imprimirá la ruta completa del script. (Por supuesto, esto supone que estás bien tomando una dependencia de 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 principal de la ruta.fuente
greadlink -f
desafortunadamente no funciona de manera efectiva cuando sesource
ejecuta el script en Mac :(La forma más corta y elegante de hacer esto es:
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? ".
fuente
Yo usaría algo como esto:
fuente
sh
también! Problema condirname "$0"
soluciones simples : si el script está en$PATH
y se invoca sin ruta, dará un resultado incorrecto.PATH
,$0
contendrá el nombre de archivo absoluto. Si la secuencia de comandos se invoca con un nombre de archivo relativo o absoluto que contiene un/
,$0
lo contendrá.Esta es una ligera revisión de la solución e-satis y 3bcdnlklvc04a señalaron en su respuesta :
Esto aún debería funcionar en todos los casos enumerados.
Esto evitará
popd
después de unpushd
error, gracias a konsolebox.fuente
SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
popd
en casos (incluso cuando es raro) dondepushd
falla. Y en caso de quepushd
falle, ¿cuál crees que debería ser el valorSCRIPT_DIR
? La acción puede variar dependiendo de lo que pueda parecer lógico o de lo que un usuario podría preferir, pero ciertamente, hacerlopopd
es incorrecto.pushd
popd
peligros podrían evitarse simplemente dejándolos caer y usandocd
+pwd
encerrado en una sustitución de comando.SCRIPT_DIR=$(...)
Para sistemas que tienen GNU coreutils
readlink
(por ejemplo, linux):No hay necesidad de usar
BASH_SOURCE
cuando$0
contiene el nombre de archivo del script.fuente
dirname
llamada. Necesario si la ruta del directorio contiene espacios.fuente
$0
no funcionará para un script de origen$_
Vale la pena mencionar como una alternativa a$0
. Si está ejecutando un script desde Bash, la respuesta aceptada se puede acortar a:Tenga en cuenta que esta debe ser la primera declaración en su secuencia de comandos.
fuente
source
o.
el guión. En esas situaciones,$_
contendría el último parámetro del último comando que ejecutó antes de.
.$BASH_SOURCE
Funciona todo el tiempo.He comparado muchas de las respuestas dadas y encuentro algunas soluciones más compactas. Estos parecen manejar todos los casos de borde loco que surgen de su combinación favorita de:
script
,bash script
,bash -c script
,source script
, o. script
Si está ejecutando desde Linux, parece que usar el
proc
identificador es la mejor solución para localizar la fuente totalmente resuelta del script que se está ejecutando actualmente (en una sesión interactiva, el enlace apunta al respectivo/dev/pts/X
):Esto tiene un poco de fealdad, pero la solución es compacta y fácil de entender. No estamos usando bash primitivos solamente, pero estoy de acuerdo con eso porque
readlink
simplifica la tarea considerablemente. Laecho X
suma unaX
al final de la cadena variable de modo que cualquier espacio en blanco final en el nombre del archivo no ser comido, y la sustitución de parámetros${VAR%X}
al final de la línea se deshace de laX
. Debido a quereadlink
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 citas, 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 desviados)Lo anterior debe 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 encuentra útil el siguiente código. Es solo una ligera modificación de la línea anterior. Si está jugando con directorios / nombres de archivo extraños, verifica la salida con ambosls
yreadlink
es informativo, ya quels
generará rutas "simplificadas", sustituyendo?
cosas como líneas nuevas.fuente
/dev/pts/30
con bash en Ubuntu 14.10 Desktop.echo $resolved
, me guardé comod
,chmod +x d
,./d
.#!/bin/bash
Intenta usar:
fuente
$0
para${BASH_SOURCE[0]}
que funcione en cualquier lugar, incluso en una función.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 en sí. La solución descrita en esta respuesta solo obtiene la ruta del directorio donde se almacenó el enlace simbólico, no el directorio del destino. Además, a esta solución le faltan citas. No funcionará si la ruta contiene caracteres especiales.dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
Pruebe la siguiente solución de compatibilidad cruzada:
ya que los comandos como
realpath
oreadlink
podrían no estar disponibles (dependiendo del sistema operativo).Nota: en Bash, se recomienda usar en
${BASH_SOURCE[0]}
lugar de$0
, de lo contrario, la ruta puede romperse al obtener el archivo (source
/.
).Alternativamente, puede probar la siguiente función en bash:
Esta función toma 1 argumento. Si el argumento ya tiene una ruta absoluta, imprímala como está, de lo contrario imprima
$PWD
variable + argumento del nombre del archivo (sin./
prefijo)Relacionado:
fuente
realpath
función @Chris toma 1 argumento. Si el argumento ya tiene una ruta absoluta, imprímala como está, de lo contrario imprima$PWD
+ nombre de archivo (sin./
prefijo).Creo que tengo este. Llego tarde a la fiesta, pero creo que algunos apreciarán que esté aquí si se encuentran con este hilo. Los comentarios deberían explicar:
fuente
Estas son formas cortas de obtener información del script:
Carpetas y archivos:
Usando estos comandos:
Y obtuve esta salida:
Ver también: https://pastebin.com/J8KjxrPF
fuente
Esto funciona en bash-3.2:
Si tiene un
~/bin
directorio en su$PATH
, tieneA
dentro de este directorio. Se origina el guión~/bin/lib/B
. Usted sabe dónde está el script incluido en relación con el original, en ellib
subdirectorio, pero no dónde está en relación con el directorio actual del usuario.Esto se resuelve con lo siguiente (dentro
A
):No importa dónde esté el usuario o cómo llame al script, esto siempre funcionará.
fuente
which
es muy discutible.type
,hash
Y otras órdenes internas hacen la misma cosa mejor en bash.which
es un poco más portátil, aunque en realidad no es el mismo que sewhich
usa en otros shells como tcsh, que lo tiene como un generador incorporado.which
Al ser una herramienta externa, no tiene ninguna razón para creer que se comporta de manera idéntica al shell principal.La mejor solución compacta en mi opinión sería:
No hay confianza en nada más que Bash. El uso de
dirname
,readlink
ybasename
eventualmente conducirá a problemas de compatibilidad, por lo que es mejor evitarlos si es posible.fuente
"$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )"
. Tendría problemas con el directorio raíz si no lo hace. Además, ¿por qué incluso tienes que usar echo?dirname
ybasename
están estandarizados POSIX, entonces ¿por qué evitar usarlos? Enlaces:dirname
,basename