¿Cómo establecer el directorio de trabajo actual en el directorio del script?

569

Estoy escribiendo un script bash. Necesito que el directorio de trabajo actual sea siempre el directorio en el que se encuentra el script.

El comportamiento predeterminado es que el directorio de trabajo actual en el script es el del shell desde el que lo ejecuto, pero no quiero este comportamiento.

jameshfisher
fuente
¿Ha considerado colocar un script de envoltura en algún lugar como / usr / bin para cd en el directorio apropiado (codificado) y luego ejecutar su script?
Dagg Nabbit
2
¿Por qué necesitas el directorio del script? Probablemente haya una mejor manera de resolver el problema subyacente.
bstpierre
12
Solo me gustaría señalar que el comportamiento que usted llama "obviamente indeseable" es de hecho completamente necesario: si ejecuto myscript path/to/file, espero que el script evalúe la ruta / a / file en relación con MI directorio actual, no el directorio en el que ocurre el script para ubicarlo. Además, ¿qué pasaría si se ejecutara un script con ssh remotehost bash < ./myscriptlas preguntas frecuentes de BASH?
Gordon Davisson el
3
cd "${BASH_SOURCE%/*}" || exit
graznar

Respuestas:

656
#!/bin/bash
cd "$(dirname "$0")"
ndim
fuente
77
dirname devuelve '.' cuando se usa bash en Windows. Entonces, la respuesta de Paul es mejor.
Tvaroh
77
También devuelve '.' en Mac OSX
Ben Clayton
44
Vale la pena señalar que las cosas pueden romperse si un enlace simbólico forma parte de él $0. En su script, puede esperar, por ejemplo, ../../referirse al directorio dos niveles por encima de la ubicación del script, pero este no es necesariamente el caso si hay enlaces simbólicos en juego.
Brian Gordon
20
Si llamó al script como ./script, .es el directorio correcto, y cambiarlo .también terminará en el mismo directorio donde scriptse encuentra, es decir, en el directorio de trabajo actual.
ndim
66
Si ejecuta el script desde el directorio actual bash script.sh, el valor de $0es script.sh. La única forma en que el cdcomando "funcionará" para usted es porque no le importan los comandos fallidos. Si tuviera que usar set -o errexit(también conocido como:) set -epara asegurarse de que su script no supere los comandos fallidos, esto NO funcionaría porque cd script.shes un error. La manera confiable [específica de bash] escd "$(dirname ${BASH_SOURCE[0]})"
Bruno Bronosky
322

Lo siguiente también funciona:

cd "${0%/*}"

La sintaxis se describe a fondo en esta respuesta de StackOverflow.

Paul Schulz
fuente
17
Explicación de cómo funciona: stackoverflow.com/questions/6393551/…
kenorb
25
No olvide encerrar entre comillas si la ruta contiene espacios en blanco. es decir, cd "$ {0% / *}"
H.Rabiee
17
Esto falla si se llama al script con bash script.sh- $0solo será el nombre del archivo
Chris Watts
17
Yo diría que esta respuesta es demasiado sucinta. Casi no aprende nada sobre la sintaxis. Alguna descripción sobre cómo leer esto sería útil.
fraxture
2
Este truco no genera una subshell también, por lo que es bueno para los fanáticos de la velocidad.
teknopaul
184

Pruebe las siguientes frases sencillas:


Para todos UNIX / OSX / Linux

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)

Golpetazo

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

Nota: Se utiliza un guión doble (-) en los comandos para indicar el final de las opciones del comando, por lo que los archivos que contienen guiones u otros caracteres especiales no romperán el comando.

Nota: en Bash, use ${BASH_SOURCE[0]}a favor de $0, de lo contrario, la ruta puede romperse al obtenerla ( source/ .).


Para Linux, Mac y otros * BSD:

cd "$(dirname "$(realpath "$0")")";

Nota: realpathdebe instalarse en la distribución Linux más popular de forma predeterminada (como Ubuntu), pero en algunos puede faltar, por lo que debe instalarla.

Nota: Si está usando Bash, use ${BASH_SOURCE[0]}a favor de $0, de lo contrario, la ruta puede romperse al obtenerla ( source/ .).

De lo contrario, podría intentar algo como eso (usará la primera herramienta existente):

cd "$(dirname "$(readlink -f "$0" || realpath "$0")")"

Para Linux específico:

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

Usando GNU readlink en * BSD / Mac:

cd "$(dirname "$(greadlink -f "$0")")"

Nota: debe tener coreutilsinstalado (por ejemplo, 1. Instalar Homebrew , 2. brew install coreutils).


En bash

En bash puedes usar las expansiones de parámetros para lograr eso, como:

cd "${0%/*}"

pero no funciona si el script se ejecuta desde el mismo directorio.

Alternativamente, puede definir la siguiente función en bash:

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

Esta función toma 1 argumento. Si el argumento ya tiene una ruta absoluta, imprímala como está; de lo contrario, imprima la $PWDvariable + argumento del nombre de archivo (sin ./prefijo).

o aquí está la versión tomada del .bashrcarchivo Debian :

function realpath()
{
    f=$@
    if [ -d "$f" ]; then
        base=""
        dir="$f"
    else
        base="/$(basename "$f")"
        dir=$(dirname "$f")
    fi
    dir=$(cd "$dir" && /bin/pwd)
    echo "$dir$base"
}

Relacionado:

Ver también:

¿Cómo puedo obtener el comportamiento de readlink -f de GNU en una Mac?

kenorb
fuente
44
una respuesta mucho mejor que las populares porque resuelve enlaces sys y da cuenta de diferentes sistemas operativos. ¡Gracias!
Dennis
Gracias por esta respuesta El superior funcionó en mi Mac ... pero ¿qué hace el interruptor - en el cpcomando, @kenorb?
TobyG
3
Se --utiliza un guión doble ( ) en los comandos para indicar el final de las opciones de comando, de modo que los archivos que contienen guiones u otros caracteres especiales no rompan el comando. Intente, por ejemplo, crear el archivo a través de touch "-test"y touch -- -test, luego elimine el archivo a través de rm "-test"y rm -- -test, y vea la diferencia.
kenorb
1
Quitaría
mi
1
@lsh Dice que solo el realpathpaquete Debian está en desuso, no GNU realpath. Si cree que no está claro, puede sugerir una edición.
kenorb
73
cd "$(dirname "${BASH_SOURCE[0]}")"

Es fácil. Funciona.

Dan Molding
fuente
1
Esta debería ser la respuesta aceptada. Funciona en OSX y Linux Ubuntu.
David Rissato Cruz
55
Esto funciona muy bien cuando la secuencia de comandos B proviene de otra secuencia de comandos A y necesita usar rutas relativas a la secuencia de comandos B. Gracias.
Enrico
55
Esto también funciona en Cygwin para aquellos de nosotros que tenemos la desgracia de tener que tocar Windows.
Bruno Bronosky
3
O biencd "${BASH_SOURCE%/*}" || exit
grazna el
Funciona en macOS Mojave
Juan Boero
6

La respuesta aceptada funciona bien para los scripts que no se han vinculado en otro lugar, como en $PATH.

#!/bin/bash
cd "$(dirname "$0")"

Sin embargo, si el script se ejecuta a través de un enlace simbólico,

ln -sv ~/project/script.sh ~/bin/; 
~/bin/script.sh

Esto se cd en el ~/bin/ directorio y no en el ~/project/directorio, lo que probablemente romperá su secuencia de comandos si el propósito de esto cdes incluir dependencias relativas a~/project/

La respuesta segura de enlace simbólico está a continuación:

#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

readlink -f es necesario para resolver la ruta absoluta del archivo potencialmente enlazado.

Las comillas son necesarias para admitir rutas de archivo que podrían contener espacios en blanco (mala práctica, pero no es seguro asumir que este no será el caso)

James McGuigan
fuente
4

Este script parece funcionar para mí:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

La línea de comando pwd hace eco de la ubicación del script como el directorio de trabajo actual, sin importar desde dónde lo ejecute.

Amardeep AC9MF
fuente
3
realpathEs poco probable que se instale en todas partes. Lo que puede no importar, dependiendo de la situación del OP.
bstpierre
Mi sistema no tiene realpathpero tiene lo readlinkque parece ser similar.
Pausado hasta nuevo aviso.
2
¿Qué dist no tiene realpath instalado por defecto? Ubuntu lo tiene.
kenorb
1
cd "`dirname $(readlink -f ${0})`"

fuente
¿Puedes explicar tu respuesta por favor?
Zulu
1
Aunque este código puede ayudar a resolver el problema, no explica por qué y / o cómo responde la pregunta. Proporcionar este contexto adicional mejoraría significativamente su valor educativo a largo plazo. Por favor, editar su respuesta para agregar explicación, incluyendo lo que se aplican limitaciones y supuestos.
Toby Speight
-5
echo $PWD

PWD es una variable de entorno.

Maverick Holdem
fuente
55
Esto solo da el directorio llamado desde, no el directorio donde está el script.
Dagelf
-6

Si solo necesita imprimir el directorio de trabajo actual, puede seguir esto.

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

Dar permiso de ejecución:

chmod u+x test

Luego ejecute el script para ./testque pueda ver el directorio de trabajo actual.

Chandan
fuente
2
La pregunta era cómo garantizar que el script se ejecute en su propio directorio, incluso si ese no es el directorio de trabajo. Entonces esta no es una respuesta. De todos modos, ¿por qué hacer esto en lugar de solo ... correr pwd? Me parecen muchas pulsaciones de teclas desperdiciadas.
underscore_d