En los casos más comunes, $0
contendrá una ruta, absoluta o relativa a la secuencia de comandos, por lo que
script_path=$(readlink -e -- "$0")
(suponiendo que haya un readlink
comando y lo admita -e
) generalmente es una forma suficientemente buena de obtener la ruta de acceso absoluta canónica al script.
$0
se asigna a partir del argumento que especifica el guión que se pasa al intérprete.
Por ejemplo, en:
the-shell -shell-options the/script its args
$0
consigue the/script
.
Cuando corres:
the/script its args
Su caparazón hará un:
exec("the/script", ["the/script", "its", "args"])
Si el script contiene un #! /bin/sh -
she-bang, por ejemplo, el sistema lo transformará a:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(si no contiene un she-bang, o más generalmente si el sistema devuelve un error ENOEXEC, entonces es su shell el que hará lo mismo)
Hay una excepción para los scripts setuid / setgid en algunos sistemas, donde el sistema abrirá el script en algunos fd
x
y se ejecutará en su lugar:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
para evitar condiciones de carrera (en cuyo caso $0
contendrá /dev/fd/x
).
Ahora, puede argumentar que /dev/fd/x
es un camino hacia ese script. Sin embargo $0
, tenga en cuenta que si lee , romperá el script a medida que consume la entrada.
Ahora, hay una diferencia si el nombre del comando de script como invocado no contiene una barra inclinada. En:
the-script its args
Su cáscara buscará the-script
en $PATH
. $PATH
puede contener rutas absolutas o relativas (incluida la cadena vacía) a algunos directorios. Por ejemplo, si $PATH
contiene /bin:/usr/bin:
y the-script
se encuentra en el directorio actual, el shell hará un:
exec("the-script", ["the-script", "its", "args"])
que se convertirá en:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
O si se encuentra en /usr/bin
:
exec("/usr/bin/the-script", ["the-script", "its", "args"])
exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
"-", "/usr/bin/the-script", "its", "args")
En todos los casos anteriores, excepto el caso de la esquina setuid, $0
contendrá una ruta (absoluta o relativa) al script.
Ahora, un script también se puede llamar como:
the-interpreter the-script its args
Cuando the-script
como arriba no contiene caracteres de barra diagonal, el comportamiento varía ligeramente de un shell a otro.
Las ksh
implementaciones antiguas de AT&T realmente buscaban el script incondicionalmente $PATH
(lo que en realidad era un error y un agujero de seguridad para los scripts setuid), por lo que en $0
realidad no contenía una ruta al script a menos que la $PATH
búsqueda realmente se encontrara the-script
en el directorio actual.
Los nuevos AT&T ksh
intentarían interpretar the-script
en el directorio actual si es legible. Si no, buscaría un archivo legible y ejecutablethe-script
en $PATH
.
Para bash
, comprueba si the-script
está en el directorio actual (y no es un enlace simbólico roto) y si no, las operaciones de búsqueda para un legible (no necesariamente ejecutable) the-script
en $PATH
.
zsh
en sh
la emulación como lo haría bash
excepto que si the-script
es un enlace simbólico roto en el directorio actual, no sería buscar un the-script
en $PATH
y en su lugar informar de un error.
Todos los otros proyectiles tipo Bourne no miran the-script
hacia adentro $PATH
.
De todos modos, para todos esos proyectiles, si encuentra que $0
no contiene /
ay no es legible, entonces probablemente se haya buscado $PATH
. Luego, dado que $PATH
es probable que los archivos sean ejecutables, es probable que sea una aproximación segura de usar command -v -- "$0"
para encontrar su ruta (aunque eso no funcionaría si $0
también fuera el nombre de un shell incorporado o una palabra clave (en la mayoría de los shells)).
Entonces, si realmente quieres cubrir ese caso, puedes escribirlo:
progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in ${PATH-$(getconf PATH)}""; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%s\n' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown
(lo que se ""
adjunta $PATH
es para preservar un elemento vacío final con conchas que $IFS
actúa como delimitador en lugar de separador ).
Ahora, hay formas más esotéricas de invocar un script. Se podría hacer:
the-shell < the-script
O:
cat the-script | the-shell
En ese caso, $0
será el primer argumento ( argv[0]
) que recibió el intérprete (arriba the-shell
, pero eso podría ser cualquier cosa, aunque generalmente sea el nombre base o una ruta a ese intérprete).
Detectar que estás en esa situación en función del valor de $0
no es confiable. Podrías mirar la salida de ps -o args= -p "$$"
para obtener una pista. En el caso de la tubería, no hay una forma real de volver a la ruta del script.
También se podría hacer:
the-shell -c '. the-script' blah blih
Entonces, excepto en zsh
(y alguna implementación antigua del shell Bourne), $0
sería blah
. Nuevamente, es difícil llegar al camino del guión en esos shells.
O:
the-shell -c "$(cat the-script)" blah blih
etc.
Para asegurarse de que tiene el derecho $progname
, puede buscar una cadena específica como:
progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in ${PATH-$(getconf PATH)}:; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%s\n' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown
[ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
progname=unknown
Pero de nuevo, no creo que valga la pena el esfuerzo.
$0
hay algo más que el guión, que responde el título de la pregunta. Sin embargo, también estoy interesado en situaciones en las que se$0
encuentra el script en sí, pero no incluye el directorio. En particular, estoy tratando de entender el comentario hecho sobre la respuesta SO.