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 )"
@City respondió que
DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
trabajos.
Yo también lo usé.
Encontré el comando en
https://.com/questions/760110/can-i-get-the-absolute-path-to-the-cu rrent-script-in-kornshell
.
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 comosh
). -
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 enzsh
, donde, como se señaló,$0
es realmente la ruta del script actual). Por el contrario (excepto enzsh
), 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
yzsh
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, enbash
,$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 conpwd
. -
Al comando le falta silenciar la salida stdout potencial de
cd
, si se establece$CDPATH
).
-
Usar
-
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 simplementedirname
volverá.
(porque eso es lo quedirname
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 elcommand -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 comosh
en un sistema dado:bash
,ksh
yzsh
devuelven una cadena vacía; solo eldash
hace eco de$0
La especificación POSIX. forcommand
no dice explícitamente sicommand -v
, cuando se aplica a una ruta del sistema de archivos , solo debe devolver archivos ejecutables , que es lo que hacenbash
,ksh
yzsh
, pero puede argumentar que está implícito en el propósito mismo delcommand
; Curiosamente, eldash
, 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
), elcommand -v -- "$0"
también devolverá la cadena vacía , excepto en eldash
.
-
si el script en sí
no
es ejecutable: el
-
Dado que el directorio del script
no se
puede determinar cuando se
obtiene
el script, ya que
$0
no contiene esa información (excepto enzsh
, que generalmente no actúa comosh
), 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" ]
-
Un enfoque más confiable sería simplemente probar
-
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 .
-
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
.
-
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 dedirname
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.
-
Cuando el shell lo obtiene
directamente
(como un archivo de perfil / inicialización de shell),
-
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 transformamyScript
en una ruta absoluta y luego lo ejecuta; p.ej:-
myScript # executes /home/jdoe/bin/myScript, for instance
-
-
usted especifica
explícitamente
una ruta
absoluta
;
p.ej:
-
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
).
-
Al invocar explícitamente
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
yksh
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.
-
En la práctica,
-
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
yzsh
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
yksh
comportan de esa manera. -
Nuevamente,
zsh
es el único disidente e informa el nombre de la función .
-
En la práctica,
-
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
yzsh
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
, nobash
, en shells interactivos en OSX.
-
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
-
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
)
-
esto también se aplica a la canalización de un archivo de script al shell mediante stdin (por ejemplo,
-
bash
,dash
,ksh
yzsh
comportan de esa manera.
-
un
shell
interactivo
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!