Quiero obtener el script actual para poder imprimir la ayuda y la información de la versión desde la sección de comentarios en la parte superior.
Estaba pensando en algo como esto:
grep '^#h ' -- "$0" | sed -e 's/#h //'
Pero luego me pregunté qué pasaría si el script se ubicara en un directorio que estaba en PATH y se llamara sin especificar explícitamente el directorio.
Busqué una explicación de las variables especiales y encontré las siguientes descripciones de $0:
nombre del shell o programa actual
nombre de archivo del script actual
nombre del guión en sí
comando como se ejecutó
Ninguno de estos aclara si el valor de $0incluiría o no el directorio si se invocara el script sin él. El último realmente me implica que no lo haría.
Prueba en mi sistema (Bash 4.1)
Creé un archivo ejecutable en / usr / local / bin llamado scriptname con una línea echo $0y lo invoqué desde diferentes ubicaciones.
Estos son mis resultados:
> cd /usr/local/bin/test
> ../scriptname
../scriptname
> cd /usr/local/bin
> ./scriptname
./scriptname
> cd /usr/local
> bin/scriptname
bin/scriptname
> cd /tmp
> /usr/local/bin/scriptname
/usr/local/bin/scriptname
> scriptname
/usr/local/bin/scriptname
En estas pruebas, el valor de $0siempre es exactamente cómo se invocó el script, excepto si se invoca sin ningún componente de ruta. En ese caso, el valor de $0es la ruta absoluta . Entonces parece que sería seguro pasar a otro comando.
Pero luego encontré un comentario sobre Stack Overflow que me confundió. La respuesta sugiere usar $(dirname $0)para obtener el directorio del script actual. El comentario (votado 7 veces) dice "eso no funcionará si el script está en su camino".
Preguntas
- ¿Es correcto ese comentario?
 - ¿Es el comportamiento diferente en otros sistemas?
 - ¿Hay situaciones en las 
$0que no incluiría el directorio? 
fuente

$0hay 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$0encuentra el script en sí, pero no incluye el directorio. En particular, estoy tratando de entender el comentario hecho sobre la respuesta SO.Respuestas:
En los casos más comunes,
$0contendrá una ruta, absoluta o relativa a la secuencia de comandos, por lo que(suponiendo que haya un
readlinkcomando y lo admita-e) generalmente es una forma suficientemente buena de obtener la ruta de acceso absoluta canónica al script.$0se asigna a partir del argumento que especifica el guión que se pasa al intérprete.Por ejemplo, en:
$0consiguethe/script.Cuando corres:
Su caparazón hará un:
Si el script contiene un
#! /bin/sh -she-bang, por ejemplo, el sistema lo transformará a:(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
fdxy se ejecutará en su lugar:para evitar condiciones de carrera (en cuyo caso
$0contendrá/dev/fd/x).Ahora, puede argumentar que
/dev/fd/xes 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:
Su cáscara buscará
the-scripten$PATH.$PATHpuede contener rutas absolutas o relativas (incluida la cadena vacía) a algunos directorios. Por ejemplo, si$PATHcontiene/bin:/usr/bin:ythe-scriptse encuentra en el directorio actual, el shell hará un:que se convertirá en:
O si se encuentra en
/usr/bin:En todos los casos anteriores, excepto el caso de la esquina setuid,
$0contendrá una ruta (absoluta o relativa) al script.Ahora, un script también se puede llamar como:
Cuando
the-scriptcomo arriba no contiene caracteres de barra diagonal, el comportamiento varía ligeramente de un shell a otro.Las
kshimplementaciones 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$0realidad no contenía una ruta al script a menos que la$PATHbúsqueda realmente se encontrarathe-scripten el directorio actual.Los nuevos AT&T
kshintentarían interpretarthe-scripten el directorio actual si es legible. Si no, buscaría un archivo legible y ejecutablethe-scripten$PATH.Para
bash, comprueba sithe-scriptestá 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-scripten$PATH.zshenshla emulación como lo haríabashexcepto que sithe-scriptes un enlace simbólico roto en el directorio actual, no sería buscar unthe-scripten$PATHy en su lugar informar de un error.Todos los otros proyectiles tipo Bourne no miran
the-scripthacia adentro$PATH.De todos modos, para todos esos proyectiles, si encuentra que
$0no contiene/ay no es legible, entonces probablemente se haya buscado$PATH. Luego, dado que$PATHes probable que los archivos sean ejecutables, es probable que sea una aproximación segura de usarcommand -v -- "$0"para encontrar su ruta (aunque eso no funcionaría si$0tambié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:
(lo que se
""adjunta$PATHes para preservar un elemento vacío final con conchas que$IFSactúa como delimitador en lugar de separador ).Ahora, hay formas más esotéricas de invocar un script. Se podría hacer:
O:
En ese caso,
$0será el primer argumento (argv[0]) que recibió el intérprete (arribathe-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
$0no es confiable. Podrías mirar la salida deps -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:
Entonces, excepto en
zsh(y alguna implementación antigua del shell Bourne),$0seríablah. Nuevamente, es difícil llegar al camino del guión en esos shells.O:
etc.
Para asegurarse de que tiene el derecho
$progname, puede buscar una cadena específica como:Pero de nuevo, no creo que valga la pena el esfuerzo.
fuente
"-"en los ejemplos anteriores. En mi experiencia, seexec("the-script", ["the-script", "its", "args"])convierteexec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"]), por supuesto, en la posibilidad de una opción de intérprete.#! /bin/sh -es el "usocmd -- somethingsiempre que no pueda garantizar quesomethingno comenzará con-" el adagio de buenas prácticas aquí aplicado/bin/sh(donde-el marcador de fin de opción es más portátil que--) consomethingser la ruta / nombre del guión. Si no usa eso para scripts setuid (en sistemas que los admiten pero no con el método / dev / fd / x mencionado en la respuesta), entonces puede obtener un shell raíz creando un enlace simbólico a su script llamado-io-spara ejemplo./bin/shdebería deshabilitar su propio procesamiento de opciones si puede detectar que se está ejecutando setuid. No veo cómo usar / dev / fd / x por sí solo soluciona eso. Todavía necesitas el guión simple / doble, creo./dev/fd/xcomienza con/, no-. Sinexecve()embargo, el objetivo principal es eliminar la condición de carrera entre los dos s (entre elexecve("the-script")cual eleva los privilegios y el posteriorexecve("interpreter", "thescript")dondeinterpreterabre el script más tarde (que bien podría haber sido reemplazado por un enlace simbólico a otra cosa mientras tanto). Sistemas que implementan los scripts de suid hacen correctamente unexecve("interpreter", "/dev/fd/n")lugar donde se ha abierto n como parte del primer execve ().Aquí hay dos situaciones en las que el directorio no se incluiría:
En ambos casos, el directorio actual tendría que ser el directorio donde se encontraba el nombre del script .
En el primer caso, el valor de
$0todavía podría pasarsegrepya que supone que el argumento FILE es relativo al directorio actual.En el segundo caso, si la información de ayuda y versión solo se imprime en respuesta a una opción de línea de comando específica, no debería ser un problema. No estoy seguro de por qué alguien invocaría un script de esa manera para imprimir la ayuda o la información de la versión.
Advertencias
Si el script cambia el directorio actual, no querrá usar rutas relativas.
Si el script tiene su origen, el valor de
$0usualmente será el script de la persona que llama en lugar del script de origen.fuente
Se puede especificar un argumento arbitrario cero cuando se usa la
-copción para la mayoría de los shells (¿todos?). P.ej:De
man bash(elegido únicamente porque tiene una mejor descripción que miman sh: el uso es el mismo independientemente):fuente
argv0es su primer argumento no operativo y de línea de comandos.NOTA: Otros ya han explicado la mecánica de,
$0así que me saltearé todo eso.Por lo general, paso por alto todo este problema y simplemente uso el comando
readlink -f $0. Esto siempre te devolverá el camino completo de lo que sea que le des como argumento.Ejemplos
Digamos que estoy aquí para empezar:
Hacer un directorio + archivo:
Ahora comienza a presumir
readlink:Trucos adicionales
Ahora, con un resultado coherente que se devuelve cuando interrogamos a
$0través dereadlink, podemos usar simplementedirname $(readlink -f $0)para obtener la ruta absoluta al script, obasename $(readlink -f $0)para obtener el nombre real del script.fuente
Mi
manpágina dice:Parece que esto se traduce en
argv[0]el shell actual, o en el primer argumento de línea de comandos no operativo y al que se alimenta el shell de interpretación actual cuando se invoca. He dicho anteriormente que elsh ./somescriptcamino sería la variable de a , pero esto era incorrecto porque el es un nuevo proceso de su propia e invocado por una nueva .$0 $ENVshshell$ENVDe esta manera
sh ./somescript.shdifiere de lo. ./somescript.shque se ejecuta en el entorno actual y$0ya está configurado.Puede verificar esto comparando
$0con/proc/$$/status.Gracias por la corrección, @toxalot. Aprendí algo
fuente
. ./myscript.shosource ./myscript.sh), entonces$0es el shell. Pero si se pasa como argumento al shell (sh ./myscript.sh), entonces$0es la ruta al script. Por supuesto,shen mi sistema está Bash. Entonces no sé si eso hace la diferencia o no.execy, en su lugar, buscarlo, pero con él, deberíaexecserlo../somescriptejecuta con el punto de referencia#!/bin/sh, sería equivalente a correr/bin/sh ./somescript. De lo contrario, no hacen ninguna diferencia en el caparazón.Si bien
$0contiene el nombre del script, puede contener una ruta prefijada basada en la forma en que se llama el script, siempre he usado${0##*/}para imprimir el nombre del script en la salida de ayuda que elimina cualquier ruta principal$0.Tomado de la guía avanzada de secuencias de comandos Bash - Sección 10.2 sustitución de parámetros
Entonces, la parte más larga de
$0esas coincidencias*/será el prefijo de ruta completo, devolviendo solo el nombre del script.fuente
Para algo similar uso:
rPathSiempre tiene el mismo valor.fuente