Consulte un archivo en el mismo directorio de un script que se encuentra en $ PATH

33

Tengo un archivo de script bash, que se coloca en algún directorio agregado a $ PATH para que pueda llamar al script desde cualquier directorio.

Hay otro archivo de texto en el mismo directorio que el script. Me pregunto cómo hacer referencia al archivo de texto en el script.

Por ejemplo, si el script es solo para generar el contenido del archivo de texto, cat textfileno funcionará, ya que cuando se llama al script desde un directorio diferente, no se encuentra el archivo de texto.

Tim
fuente
Esta pregunta responde cómo obtener una ruta de archivos de script bash de manera confiable, lo agregué a mi ruta: stackoverflow.com/q/4774054/1695680
ThorSummoner

Respuestas:

24

Estos deberían funcionar igual, siempre que no haya enlaces simbólicos (en la expansión de ruta o en el script en sí):

  • MYDIR="$(dirname "$(realpath "$0")")"

  • MYDIR="$(dirname "$(which "$0")")"

  • Una versión de dos pasos de cualquiera de los anteriores:

    MYSELF="$(realpath "$0")"

    MYDIR="${MYSELF%/*}"

Si hay un enlace simbólico en el camino a su secuencia de comandos, whichproporcionará una respuesta sin incluir la resolución de ese enlace. Si realpathno está instalado por defecto en su sistema, puede encontrarlo aquí .

[EDITAR]: Como parece que realpathno tiene ninguna ventaja sobre lo readlink -f sugerido por Caleb , probablemente sea mejor usar este último. Mis pruebas de tiempo indican que en realidad es más rápido.

rozcietrzewiacz
fuente
No hay problema. Por cierto, de dónde realpathviene en su sistema. (Para otros que no lo tienen, puede usarreadlink -f
Caleb
@Caleb En realidad, pensé que pertenecía al conjunto de utilidades GNU estándar (coreutils), pero ahora puedo ver que es un paquete separado .
rozcietrzewiacz
@rozcietrzewiacz realpathse remonta a antes readlink -f(e incluso readlink, IIRC) estaba en GNU coreutils (había varias herramientas similares alrededor. readlink -feventualmente se convirtió en el estándar de facto); realpathsolo se mantiene por compatibilidad con scripts que aún lo usan.
Gilles 'SO- deja de ser malvado'
¿Cuál es la ventaja de $(dirname "$(which "$0")")sobre $(dirname $0)donde whichya no está presente? ¿No es lo mismo?
UlfR
readlink -fno parece funcionar en Mac OS X 10.11.6, pero realpathfunciona de inmediato .
Grav
9

Mis sistemas no tienen realpathlo sugerido por rozcietrzewiacz .

Puede lograr esto usando el readlinkcomando. La ventaja de usar esto sobre el análisis whichu otras soluciones es que incluso si una parte de la ruta o el nombre de archivo ejecutado fuera un enlace simbólico, podría encontrar el directorio donde estaba el archivo real.

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

Su archivo de texto podría leerse en una variable como esta:

TEXTFILE="$(<$MYDIR/textfile)"
Caleb
fuente
@rozcietrzewiacz: en realidad no me refería solo a su whichsugerencia. La solución normal para esto implica solo dirnameo una combinación de cdy pwden una subshell. Readlink tiene la ventaja aquí. realpathparece ser más o menos una envoltura para readlink -ftodos modos.
Caleb
No sé cómo realpathes diferente readlink -f. Solo puedo ver que me da los mismos resultados (en lugar de which).
rozcietrzewiacz
Tenga en cuenta que readlink -f(de GNU coreutils) NO requiere que exista el último elemento de la ruta, lo readlink -ehace, pero no es compatible busybox readlink, lo que imita el comportamiento de -esu -fopción.
dragon788
8

$0en la secuencia de comandos será la ruta completa a la secuencia de comandos, y dirnametomará una ruta completa y le dará solo el directorio, para que pueda hacer esto al archivo de texto cat:

$ cat "$(dirname -- "$0")/textfile"
Michael Mrozek
fuente
Si bien esto parece funcionar sin realpath $0, está equivocado al decir que " $0en la secuencia de comandos será la ruta completa a la secuencia de comandos".
rozcietrzewiacz
@roz ¿De qué manera?
Michael Mrozek
1
$0es el comando que fue ejecutado, que puede ser, por ejemplo ../script.sh.
rozcietrzewiacz
Entonces, en realidad $(dirname "$0")devuelve la ruta relativa al script, como parte del comando invocado, no la ruta absoluta. Esto puede generar problemas en los scripts que cambian los directorios mientras se ejecutan.
rozcietrzewiacz
@roz Ah, interesante. Entonces supongo que no causaría un problema aquí ya que está llamando a algo en el camino por su nombre, pero rompería otras cosas. Gracias
Michael Mrozek
4

Puedes poner esto en la parte superior de tu script:

cd "${BASH_SOURCE%/*}" || exit

La variable bash interna BASH_SOURCE es en realidad una matriz de nombres de ruta. Si lo expande como una cadena simple, por ejemplo, "$ BASH_SOURCE", obtendrá el primer elemento, que es el nombre de ruta de la función o script que se está ejecutando actualmente.

Fuente: http://mywiki.wooledge.org/BashFAQ/028

David Kennedy
fuente
2

Estaba probando esto y Realpath no funcionó para mí. La solución con la que fui es:

SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

Lo que ha funcionado bien hasta ahora. Me gustaría saber si hay algún problema potencial con el enfoque.

Brettski
fuente
1
Bien, pero no sigue enlaces simbólicos. Pruebe esto:SCRIPT_DIR="$( cd "$(dirname "$( readlink -f ${BASH_SOURCE[0]} )")" >/dev/null 2>&1 && pwd)"
OronNavon
1

Yo suelo:

#! /bin/sh -
dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P) || exit
dosomethingwith "${dir%/}/some-file"

Que es POSIX y debería funcionar siempre que el nombre de directorio de $0no termine en caracteres de nueva línea, no esté -y $CDPATHno esté configurado (y posiblemente algunos otros casos de esquina si no se buscó el script $PATH).

Stéphane Chazelas
fuente
0

Siempre uso whichpara encontrar la ruta completa de un ejecutable desde la RUTA. Por ejemplo:

which python

Si combina esto con el dirnamecomando, obtendrá:

wp=`which python`
dn=`dirname $wp`
ls $dn
Michael Dillon
fuente