#! / bin / sh -
Es (o al menos fue) a menudo el shebang recomendado para que un script sea interpretado /bin/sh
.
¿Por qué no solo #! /bin/sh
o #!/bin/sh
?
¿Para qué es eso -
?
#! / bin / sh -
Es (o al menos fue) a menudo el shebang recomendado para que un script sea interpretado /bin/sh
.
¿Por qué no solo #! /bin/sh
o #!/bin/sh
?
¿Para qué es eso -
?
Respuestas:
Eso es por una razón similar a la que necesita escribir:
rm -- *.txt
Y no
rm *.txt
A menos que pueda garantizar que ninguno de los .txt
archivos del directorio actual tenga un nombre que comience por -
.
En:
rm <arg>
<arg>
se considera una opción si comienza con -
o un archivo para eliminar de lo contrario. En
rm - <arg>
arg
siempre se considera como un archivo para eliminar, independientemente de si comienza con -
o no.
Eso es lo mismo para sh
.
Cuando uno ejecuta un script que comienza con
#! /bin/sh
Típicamente con:
execve("path/to/the-script", ["the-script", "arg"], [environ])
El sistema lo transforma a:
execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])
Por path/to/the-script
lo general, no es algo que esté bajo el control del autor del script. El autor no puede predecir dónde se almacenarán las copias del guión ni con qué nombre. En particular, no pueden garantizar que la path/to/the-script
con la que se llama no comenzará -
(o +
que también es un problema sh
). Es por eso que necesitamos el -
aquí para marcar el final de las opciones.
Por ejemplo, en mi sistema zcat
(como la mayoría de los otros scripts en realidad) es un ejemplo de un script que no siguió esa recomendación:
$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]
Ahora puede preguntar por qué #! /bin/sh -
y no #! /bin/sh --
.
Si bien #! /bin/sh --
funcionaría con shells POSIX, #! /bin/sh -
es más portátil; en particular a las versiones antiguas de sh
. sh
tratar -
como y las fechas anteriores al final de la opción getopt()
y el uso general de --
para marcar el final de las opciones por un largo tiempo. La forma en que el shell Bourne (de finales de los 70) analizó sus argumentos, solo se consideró el primer argumento para las opciones si comenzaba con -
. Todos los caracteres posteriores -
se tratarán como nombres de opciones; Si no había ningún personaje después del -
, no había opciones. Eso se atascó y todos los proyectiles similares a Bourne posteriores reconocen -
como una forma de marcar el final de las opciones.
En el shell Bourne (pero no en los shells modernos tipo Bourne), #! /bin/sh -euf
también se solucionaría el problema, ya que solo se consideró el primer argumento para las opciones.
Ahora, uno podría decir que estamos siendo pedantes aquí, y también es por eso que escribí la necesidad en cursiva arriba:
-
o +
o los coloque en un directorio cuyo nombre comience con -
o +
.execvp()
/ execlp()
-type. Y en ese caso, generalmente los invoca the-script
para que se busquen, en $PATH
cuyo caso el argumento de la ruta a la execve()
llamada del sistema generalmente comenzará con /
(no -
ni +
) o como ./the-script
si desea the-script
que se ejecute en el directorio actual (y entonces el camino comienza con ./
, -
ni +
tampoco).Ahora, además del problema de la corrección teórica , hay otra razón por la cual #! /bin/sh -
se recomienda como buena práctica . Y eso se remonta a una época en la que varios sistemas todavía admitían scripts setuid.
Si tiene un script que contiene:
#! /bin/sh
/bin/echo "I'm running as root"
Y ese script era raíz setuid (como con -r-sr-xr-x root bin
permisos), en esos sistemas, cuando es ejecutado por un usuario común,
execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])
se haría como root
!
Si el usuario creó un enlace simbólico /tmp/-i -> path/to/the-script
y lo ejecutó como -i
, entonces comenzaría un shell interactivo ( /bin/sh -i
) como root
.
Esto -
funcionaría alrededor de eso (no funcionaría alrededor del problema de la condición de carrera , o el hecho de que algunas sh
implementaciones como las ksh88
basadas en algunas buscarían argumentos de script sin /
en $PATH
).
Hoy en día, casi ningún sistema admite el script setuid, y algunos de los que aún lo hacen (generalmente no de forma predeterminada), terminan haciendo un execve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])
(donde <n>
hay un descriptor de archivo abierto para leer en el script) que funciona tanto para este problema como para el condición de carrera.
Tenga en cuenta que tiene problemas similares con la mayoría de los intérpretes, no solo con los shells tipo Bourne. Los shells que no son de Bourne generalmente no son compatibles -
con el marcador de fin de opción, pero generalmente son compatibles --
(al menos para las versiones modernas).
también
#! /usr/bin/awk -f
#! /usr/bin/sed -f
¿No tiene el problema ya que el siguiente argumento es considerado como un argumento a la -f
opción en cualquier caso, pero aún así no funciona si path/to/script
es -
(en cuyo caso, la mayoría de los sed
/ awk
las implementaciones, sed
/ awk
no lea el código de la -
archivo, pero de stdin en su lugar).
También tenga en cuenta que en la mayoría de los sistemas, uno no puede usar:
#! /usr/bin/env sh -
#! /usr/bin/perl -w --
Como en la mayoría de los sistemas, el mecanismo shebang solo permite un argumento después de la ruta del intérprete.
En cuanto a si usar #! /bin/sh -
vs #!/bin/sh -
, eso es solo cuestión de gustos. Prefiero el primero ya que hace que la ruta del intérprete sea más visible y facilita la selección del mouse. Hay una leyenda que dice que el espacio era necesario en algunas versiones antiguas de Unix, pero AFAIK, que nunca se ha verificado.
Puede encontrar una muy buena referencia sobre el shebang de Unix en https://www.in-ulm.de/~mascheck/various/shebang
bash
acepta opciones como cualquier otro shell. Al ser un shell Bourne-like y POSIX, bash
acepta ambos #! /bin/bash -
y #! /bin/bash --
.