script current bash posix sh

bash - current - ¿Cómo obtener el directorio de script en POSIX sh?



get local path bash (3)

Tengo el siguiente código en mi script bash. Ahora quiero usarlo en POSIX sh. Entonces, ¿cómo convertirlo? Gracias.

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



La contraparte POSIX-shell ( sh ) de $BASH_SOURCE es $0 . ver abajo para información de fondo

Advertencia : la diferencia crucial es que si su script se obtiene (cargado en el shell actual con) , los fragmentos a continuación no funcionarán correctamente. explicación más abajo

Tenga en cuenta que he cambiado DIR a dir en los fragmentos a continuación, porque es mejor no usar nombres de variables en mayúsculas para evitar conflictos con variables de entorno y variables especiales de shell.
El prefijo CDPATH= toma el lugar de > /dev/null en el comando original: $CDPATH se establece en una cadena nula para garantizar que cd nunca repita nada.

En el caso más simple, esto hará (el equivalente al comando del OP ):

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

Si también desea resolver la ruta del directorio resultante a su objetivo final en caso de que el directorio y / o sus componentes sean enlaces simbólicos , agregue -P al comando pwd :

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

Advertencia : esto NO es lo mismo que encontrar el verdadero directorio de origen del script :
Digamos que su script foo está vinculado a /usr/local/bin/foo en $PATH , pero su verdadera ruta es /foodir/bin/foo .
Lo anterior aún informará /usr/local/bin , porque la resolución del enlace simbólico ( -P ) se aplica al directorio , /usr/local/bin , en lugar de al script en sí.

Para encontrar el verdadero directorio de origen del script, debe inspeccionar la ruta del script para ver si es un enlace simbólico y, de ser así, seguir los enlaces simbólicos (en cadena) hasta el archivo de destino final y luego extraer la ruta del directorio desde la ruta canónica del archivo de destino .

readlink -f GNU (mejor: readlink -e ) podría hacer eso por usted, pero readlink no es una utilidad POSIX.
Si bien las plataformas BSD, incluido OSX, también tienen una utilidad readlink , en OSX no admite la funcionalidad de -f . Dicho esto, para mostrar cuán simple se vuelve la tarea si readlink -f está disponible: dir=$(dirname "$(readlink -f -- "$0")") .

De hecho, no existe una utilidad POSIX para resolver enlaces simbólicos de archivos . Hay formas de evitarlo, pero son engorrosos y no completamente robustos:

La siguiente función de shell compatible con POSIX implementa lo que hace readlink -e GNU y es una solución razonablemente robusta que solo falla en dos casos extremos :

  • caminos con nuevas líneas incrustadas (muy raro)
  • nombres de archivo que contienen cadenas literales -> (también raro)

Con esta función, denominada rreadlink , definida, lo siguiente determina la verdadera ruta de origen del directorio del script :

dir=$(dirname -- "$(rreadlink "$0")")

rreadlink() fuente rreadlink() : colocar antes de las llamadas en scripts:

rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`. target=$1 fname= targetDir= CDPATH= # Try to make the execution environment as predictable as possible: # All commands below are invoked via `command`, so we must make sure that `command` # itself is not redefined as an alias or shell function. # (Note that command is too inconsistent across shells, so we don''t use it.) # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have # an external utility version of it (e.g, Ubuntu). # `command` bypasses aliases and shell functions and also finds builtins # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that # to happen. { /unalias command; /unset -f command; } >/dev/null 2>&1 [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too. while :; do # Resolve potential symlinks until the ultimate target is found. [ -L "$target" ] || [ -e "$target" ] || { command printf ''%s/n'' "ERROR: ''$target'' does not exist." >&2; return 1; } command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path. fname=$(command basename -- "$target") # Extract filename. [ "$fname" = ''/'' ] && fname='''' # !! curiously, `basename /` returns ''/'' if [ -L "$fname" ]; then # Extract [next] target path, which may be defined # *relative* to the symlink''s own directory. # Note: We parse `ls -l` output to find the symlink target # which is the only POSIX-compliant, albeit somewhat fragile, way. target=$(command ls -l "$fname") target=${target#* -> } continue # Resolve [next] symlink target. fi break # Ultimate target reached. done targetDir=$(command pwd -P) # Get canonical dir. path # Output the ultimate target''s canonical path. # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path. if [ "$fname" = ''.'' ]; then command printf ''%s/n'' "${targetDir%/}" elif [ "$fname" = ''..'' ]; then # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the ''..'' is applied # AFTER canonicalization. command printf ''%s/n'' "$(command dirname -- "${targetDir}")" else command printf ''%s/n'' "${targetDir%/}/$fname" fi )

Para ser robusta y predecible, la función utiliza el command para garantizar que solo se invoquen las funciones internas de shell o las utilidades externas (ignora las sobrecargas en forma de alias y funciones).
Se ha probado en versiones recientes de los siguientes shells: bash , dash , ksh , zsh .

Cómo manejar invocaciones de origen :

tl; dr :

Usar solo las funciones de POSIX :

  • No puede determinar la ruta del script en una invocación de origen (excepto en zsh , que, sin embargo, no suele actuar como sh ).
  • Puede detectar si su script se obtiene o no SOLO si su script se obtiene directamente del shell (como en un perfil de shell / archivo de inicialización; posiblemente a través de una cadena de fuentes), comparando $0 con el nombre ejecutable del shell / ruta (excepto en zsh , donde, como se señaló, $0 es realmente la ruta del script actual). Por el contrario (excepto en zsh ), un script que se obtiene de otro script que se invocó directamente , contiene la ruta de ese script en $0 .
  • Para resolver estos problemas, bash , ksh y zsh tienen características no estándar que permiten determinar la ruta real del script incluso en escenarios de origen y también detectar si un script se obtiene o no; por ejemplo, en bash , $BASH_SOURCE siempre contiene la ruta del script en ejecución, ya sea que se obtenga o no, y [[ $0 != "$BASH_SOURCE" ]] se puede usar para probar si el script se obtiene.

Para mostrar por qué esto no se puede hacer , analicemos el comando de la respuesta de Walter A :

# NOT recommended - see discussion below. DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )

  • (Dos apartados:
    • Usar -P dos veces es redundante: es suficiente usarlo con pwd .
    • Al comando le falta silenciar la salida stdout potencial de cd , si se establece $CDPATH ).
  • command -v -- "$0"
    • command -v -- "$0" está diseñado para cubrir un escenario adicional: si el script se obtiene de un shell interactivo , $0 generalmente contiene el mero nombre de archivo del ejecutable del shell ( sh ), en cuyo caso simplemente dirname volverá . (porque eso es lo que dirname invariablemente hace cuando se le da un argumento sin un componente de ruta ). command -v -- "$0" luego devuelve la ruta absoluta de ese shell a través de una búsqueda $PATH ( /bin/sh ). Sin embargo, tenga en cuenta que los shells de inicio de sesión en algunas plataformas (por ejemplo, OSX) tienen su nombre de archivo con el prefijo - en $0 ( -sh ), en cuyo caso el command -v -- "$0" no funciona como se esperaba (devuelve una cadena vacía )
    • Por el contrario, el command -v -- "$0" puede comportarse mal en dos escenarios no abastecidos en los que el ejecutable de shell, sh , se invoca directamente , con el script como argumento:
      • si el script en sí no es ejecutable: el command -v -- "$0" puede devolver una cadena vacía , dependiendo de qué shell específico actúa como sh en un sistema dado: bash , ksh y zsh devuelven una cadena vacía; solo el dash hace eco de $0
        La especificación POSIX. for command no dice explícitamente si command -v , cuando se aplica a una ruta del sistema de archivos , solo debe devolver archivos ejecutables , que es lo que hacen bash , ksh y zsh , pero puede argumentar que está implícito en el propósito mismo del command ; Curiosamente, el dash , que generalmente es el ciudadano POSIX más compatible, se está desviando del estándar aquí. Por el contrario, ksh es el único ciudadano modelo aquí, porque es el único que informa solo de archivos ejecutables y los informa con una ruta absoluta (aunque no normalizada), como lo requiere la especificación.
      • si el script es ejecutable, pero no está en $PATH , y la invocación usa su simple nombre de archivo (por ejemplo, sh myScript ), el command -v -- "$0" también devolverá la cadena vacía , excepto en el dash .
    • Dado que el directorio del script no se puede determinar cuando se obtiene el script, ya que $0 no contiene esa información (excepto en zsh , que generalmente no actúa como sh ), no hay una buena solución para este problema.
      • Devolver la ruta del directorio del ejecutable del shell en esa situación es de uso limitado, después de todo, no es el directorio del script, excepto quizás para usar más tarde esa ruta en una prueba para determinar si el script se obtiene o no .
        • Un enfoque más confiable sería simplemente probar $0 directamente: [ "$0" = "sh" ] || [ "$0" = "-sh" ] || [ "$0" = "/bin/sh" ] [ "$0" = "sh" ] || [ "$0" = "-sh" ] || [ "$0" = "/bin/sh" ]
      • Sin embargo, incluso eso no funciona si el script se obtiene de otro script (que se invocó directamente ), porque $0 simplemente contiene la ruta del script de abastecimiento .
    • Dada la utilidad limitada del command -v -- "$0" en escenarios con fuente y el hecho de que rompe dos escenarios sin fuente, mi voto es por NO usarlo , lo que nos deja con:
      • Todos los escenarios no abastecidos están cubiertos.
      • En las invocaciones de origen , no puede determinar la ruta del script y, en el mejor de los casos, en circunstancias limitadas, puede detectar si se está produciendo o no :
        • Cuando el shell lo obtiene directamente (como un archivo de perfil / inicialización de shell), $dir termina conteniendo . , si el ejecutable del shell se invocó como un simple nombre de archivo ( siempre se devuelve la aplicación de dirname a un simple nombre de archivo), o la ruta del directorio del ejecutable del shell de lo contrario. . no se puede distinguir de manera confiable de una invocación no proveniente del directorio actual.
        • Cuando se obtiene de otra secuencia de comandos (que también no se obtuvo), $0 contiene la ruta de esa secuencia de comandos, y la secuencia de comandos que se obtiene no tiene forma de saber si ese es el caso.

Información de fondo:

POSIX define el comportamiento de $0 con respecto a los scripts de shell aquí .

Esencialmente, $0 debe reflejar la ruta del archivo de script como se especifica , lo que implica:

  • NO confíe en $0 contiene una ruta absoluta .
  • $0 contiene una ruta absoluta solo si:

    • usted especifica explícitamente una ruta absoluta ; p.ej:
      • ~/bin/myScript (suponiendo que el script en sí es ejecutable)
      • sh ~/bin/myScript
    • Invoca un script ejecutable por simple nombre de archivo , lo que requiere que sea ejecutable y en $PATH ; detrás de escena, el sistema transforma myScript en una ruta absoluta y luego lo ejecuta; p.ej:
      • myScript # executes /home/jdoe/bin/myScript, for instance
  • En todos los demás casos, $0 reflejará la ruta del script como se especifica :

    • Al invocar explícitamente sh con un script, este puede ser un simple nombre de archivo (p. Ej., sh myScript ) o una ruta relativa (p. Ej., sh ./myScript )
    • Al invocar un script ejecutable directamente , esta puede ser una ruta relativa (por ejemplo, ./myScript ; tenga en cuenta que un simple nombre de archivo solo encontrará scripts en $PATH ).

En la práctica, bash , dash , ksh y zsh exhiben este comportamiento.

Por el contrario, POSIX NO exige el valor de $0 cuando se obtiene un script (utilizando la utilidad incorporada especial ("punto")), por lo que no puede confiar en él y, en la práctica, el comportamiento difiere entre shells.

  • Por lo tanto, no puede usar $0 ciegas cuando su script se obtiene y esperar un comportamiento estandarizado.
    • En la práctica, bash , dash y ksh dejan intactos $0 al buscar secuencias de comandos, lo que significa que $0 contiene el valor de $0 la persona que llama o, más exactamente, el valor de $0 de la persona que llama más recientemente en la cadena de llamadas que no se ha obtenido; por lo tanto, $0 puede apuntar al ejecutable del shell o a la ruta de otro script (invocado directamente) que obtuvo el actual.
    • Por el contrario, zsh , como el único disidente, en realidad informa la ruta del script actual en $0 . Por el contrario, $0 no proporcionará ninguna indicación sobre si el script se está obteniendo o no.
    • En resumen: al usar solo las características de POSIX, no se puede saber de manera confiable si el script en cuestión se obtiene, ni cuál es la ruta del script en cuestión, ni cuál es la relación de $0 con la ruta del script actual.
  • Si necesita manejar esta situación, debe identificar el shell específico disponible y acceder a sus características no estándar específicas :
    • bash , ksh y zsh ofrecen sus propias formas de obtener la ruta del script en ejecución, incluso cuando se obtiene.

En aras de la integridad: el valor de $0 en otros contextos :

  • Dentro de una función de shell , POSIX exige que $0 permanezca sin cambios ; por lo tanto, cualquier valor que tenga fuera de la función, también lo tendrá dentro .
    • En la práctica, bash , dash y ksh comportan de esa manera.
    • Nuevamente, zsh es el único disidente e informa el nombre de la función .
  • En un shell que acepta una cadena de comando a través de la opción -c en el inicio, es el primer operando (argumento sin opción) que establece $0 ; p.ej:
    • sh -c ''echo /$0: $0 /$1: $1'' foo one # -> ''$0: foo $1: one''
    • bash , dash , ksh y zsh comportan de esa manera.
  • De lo contrario , en un shell que no ejecuta un archivo de script , $0 es el valor del primer argumento que pasó el proceso padre del shell ; por lo general, ese es el nombre o la ruta del shell (por ejemplo, sh , o /bin/sh ); esto incluye:
    • un shell interactivo
      • Advertencia : algunas plataformas, especialmente OSX, siempre crean shells de inicio de sesión al crear shells interactivos y anteponen el nombre del shell antes de colocarlo en $0 , para indicarle al shell que es un shell de inicio de sesión; por lo tanto, por defecto, $0 informa -bash , no bash , en shells interactivos en OSX.
    • un shell que lee comandos de stdin
      • esto también se aplica a la canalización de un archivo de script al shell mediante stdin (por ejemplo, sh < myScript )
    • bash , dash , ksh y zsh comportan de esa manera.

if OLDPWD=/dev/fd/0 / cd - && ls -lLidFH ? then cd . <8 fi </proc/self/fd 8<. 9<$0

ahí. eso debería permitirle cambiar directpry a través de algunos enlaces mágicos como descriptores de archivo.

establecer $OLDPWD pre cd exporta el valor para la duración del directorio de un cambio (nota: cd puede tener efectos residuales en las tablas hash , pero lo único que sé de que los hombres realmente usan bien es kevin ahlmquists - y desde herbert xu - dash , y tal vez algunas cosas de bsd , pero ¿qué sé yo?) pero no bsd ninguna exportación de cd como resultado del cambio.

por lo tanto, $OLDPWD no cambia, en realidad, y si tiene algún valor, todo permanece como estaba. $PWD se cambia como resultado del primer cd , y el valor se convierte en /dev/fd/0 que apunta a /proc/self/fd , donde debería estar una lista de descriptores de archivo para nuestro proceso . , para incluir lo que sea $0 en ./2 .

entonces hacemos un ls ... ? y echar un vistazo a toda la información maravillosa que podemos obtener, y vamos de donde vinimos.

¡Hurra!