¿Qué intérprete de shell ejecuta un script sin shebang?

16

Supongamos que el shell predeterminado para mi cuenta es zsh, pero abrí el terminal y activé bash y ejecuté un script llamado prac002.sh, ¿qué intérprete de shell se usaría para ejecutar el script, zsh o bash? Considere el siguiente ejemplo:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

** EDITAR: ** Aquí está el contenido del guión

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname
7_R3X
fuente
¿Qué dice el guión en la parte superior?
Michael Homer
@MichaelHomer: Nada. Acabo de editar la pregunta para agregar el contenido del guión. El script tiene permiso ejecutable.
7_R3X
Si desea utilizar el shell actual, póngalo en origen, como en . prac002.sh, suponiendo que su script esté en el directorio actual.
thecarpy
1
Ejecutar . ./prac002.shy se ejecutará con el shell actual, es decir, dot ( .), espacio () seguido de la ruta de su script. Se llama dot-sourcing de su script. ;-)
thecarpy

Respuestas:

18

Debido a que el script no comienza con una #!línea shebang que indica qué intérprete usar, POSIX dice que :

Si la execl()función falla debido a un error equivalente al error [ENOEXEC] definido en el volumen de Interfaces del sistema de POSIX.1-2008, el shell ejecutará un comando equivalente a tener un shell invocado con el nombre de ruta resultante de la búsqueda como su primer operando , con cualquier argumento restante pasado al nuevo shell, excepto que el valor de "$ 0" en el nuevo shell puede establecerse en el nombre del comando. Si el archivo ejecutable no es un archivo de texto, el shell puede omitir la ejecución de este comando. En este caso, escribirá un mensaje de error y devolverá un estado de salida de 126.

Esa redacción es un poco ambigua, y las diferentes conchas tienen diferentes interpretaciones.

En este caso, Bash ejecutará el script usándose a sí mismo . Por otro lado, si lo ejecutó desde zsh, zsh usaríash (lo que sea que esté en su sistema) en su lugar.

Puede verificar ese comportamiento para este caso agregando estas líneas al script:

echo $BASH_VERSION
echo $ZSH_VERSION

Notarás que, desde Bash, la primera línea emite tu versión, mientras que la segunda nunca dice nada, sin importar qué shell uses.

  • Si su /bin/shes, digamos, dashentonces ninguna línea generará nada cuando el script se ejecute desde zsh o dash.
  • Si su /bin/shes un enlace a Bash, verá la salida de la primera línea en todos los casos.
  • Si /bin/shes una versión diferente de Bash de la que estaba usando directamente, verá resultados diferentes cuando ejecute el script desde bash directamente y desde zsh.

El ps -p $$comando de la respuesta de rools también mostrará información útil sobre el comando que el shell usó para ejecutar el script.

Michael Homer
fuente
Sin embargo, he votado a favor, utiliza el SHELL PREDETERMINADO si no se especifica shebang, sea cual sea el shell predeterminado que haya especificado. Es por eso que siempre, en mi humilde opinión, debe especificar un shebang.
thecarpy
44
No es así, lo cual fue realmente el punto completo de la respuesta. Su segunda oración es ciertamente cierta.
Michael Homer
tienes razón, bash lo ejecuta usando él mismo, el shell predeterminado en la mayoría de mis cuadros es ... bash, de ahí mi confusión. Acabo de probar en Solaris 8 con ksh como shell predeterminado, efectivamente, bash lo ejecuta como bash ... aprendí algo nuevo hoy, gracias (¡votado!) ;-)
thecarpy
Gracias. ¿La falla de la execl()llamada ocurre cuando un script de shell no contiene un shebang y se ejecuta como scriptname? ¿No sucede cuando un script de shell se ejecuta como bash scriptname? ¿No sucede cuando un script de shell contiene un shebang y se ejecuta como scriptname?
StackExchange para todos el
Hice una publicación en unix.stackexchange.com/questions/439376/…
StackExchange for All
7

Dado que el archivo no es de ninguno de los tipos de ejecutables reconocidos por el sistema, y ​​suponiendo que tenga el permiso para ejecutar ese archivo, la execve()llamada al sistema generalmente fallará con un error ENOEXEC( no ejecutable ).

Lo que sucede entonces depende de la aplicación y / o función de biblioteca utilizada para ejecutar el comando.

Puede ser, por ejemplo, un shell, la función execlp()/ execvp()libc.

La mayoría de las otras aplicaciones usarán cualquiera de ellas cuando ejecuten un comando. Invocarán un shell, por ejemplo, mediante la system("command line")función libc, que normalmente invocará shpara analizar esa línea de comando (cuya ruta se puede determinar en el momento de la compilación (como /bin/shvs /usr/xpg4/bin/shen Solaris)), o invocará el shell almacenado $SHELLpor ellos mismos como vicon su !comando, xterm -e 'command line'y muchos otros comandos ( su user -cinvocarán el shell de inicio de sesión del usuario en lugar de $SHELL).

En general, un archivo de texto sin shebang que no comienza #se considera un shscript. Sin shembargo, cuál es variará.

execlp()/ execvp(), al execve()regresar ENOEXECnormalmente invocará shen él. Para los sistemas que tienen más de uno shporque pueden ajustarse a más de un estándar, lo shque se determinará generalmente en el momento de la compilación (de la aplicación usando execvp()/ execlp()mediante la vinculación de un blob de código diferente que se refiere a una ruta diferente a sh). Por ejemplo, en Solaris, será /usr/xpg4/bin/sh(un estándar, POSIX sh) o /bin/sh(el shell Bourne (un shell anticuado) en Solaris 10 y versiones anteriores, ksh93 en Solaris 11).

Cuando se trata de conchas, hay mucha variación. bash, AT&T ksh, el shell Bourne generalmente interpretará la secuencia de comandos ellos mismos (en un proceso secundario a menos que execse use) después de haber simulado un execve(), que desarma todas las variables no exportadas, cierra todos los archivos close-on-exec, elimina todas las trampas personalizadas, alias, funciones ... ( bashinterpretará el script en shmodo). yashse ejecutará en sí (con shcomo argv[0]lo que en shmodo) para interpretarlo.

zsh, pdksh, ashCáscaras basados típicamente invocar sh(el camino de los cuales determina en tiempo de compilación).

Para cshy tcsh(y el shde algunos BSD anteriores), si el primer carácter del archivo es #, entonces se ejecutarán para interpretarlo, y de lo shcontrario. Eso se remonta a un tiempo anterior al shebang donde cshreconocía #como comentarios pero no el shell Bourne, por lo que #era una pista de que era un script csh.

fish(al menos la versión 2.4.0), solo devuelve un error si execve()falla (no intenta tratarlo como un script).

Algunos shells (como bashAT&T ksh) primero intentarán determinar heurísticamente si el archivo probablemente sea un script o no. Por lo tanto, es posible que algunos shells se nieguen a ejecutar un script si tiene un carácter NUL en los primeros bytes.

También tenga en cuenta que si execve()falla con ENOEXEC pero el archivo tiene una línea shebang, algunos shells intentan interpretar esa línea shebang ellos mismos.

Entonces, algunos ejemplos:

  • Cuando $SHELLes /bin/bash, xterm -e 'myscript with args'habrá myscriptinterpretado por bashen shmodo. Mientras que con xterm -e myscript with args, xtermse usará execvp()para que el guión sea interpretado por sh.
  • su -c myscripten Solaris 10 donde rootel shell de inicio de sesión es /bin/shy /bin/shes el shell Bourne lo habrá myscriptinterpretado el shell Bourne.
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'en Solaris 10 lo tendrá interpretado por /usr/xpg4/bin/sh(lo mismo para /usr/xpg4/bin/env myscript).
  • find . -prune -exec myscript {} \;en Solaris 10 (usando execvp()) lo interpretará /bin/shincluso con /usr/xpg4/bin/find, incluso en un entorno POSIX (un error de conformidad).
  • csh -c myscriptlo interpretará cshsi comienza con #, de lo shcontrario.

Con todo, no puede estar seguro de qué shell se usará para interpretar ese script si no sabe cómo y por qué se invocará.

En cualquier caso, read -pes una bashsintaxis única, por lo que querrá asegurarse de que el script sea interpretado por bash(y evitar esa .shextensión engañosa ). O conoce la ruta del bashejecutable y usa:

#! /path/to/bash -
read -p ...

O puede intentar confiar en una $PATHbúsqueda del bashejecutable (suponiendo que bashesté instalado) usando:

#! /usr/bin/env bash
read -p ...

(se envencuentra casi en todas partes /usr/bin). Alternativamente, puede hacer que sea POSIX + Bourne compatible, en cuyo caso puede usar /bin/sh. Todos los sistemas tendrán a /bin/sh. En la mayoría de ellos será (en su mayor parte) compatible con POSIX, pero aún puede encontrar de vez en cuando un shell Bourne allí.

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"
Stéphane Chazelas
fuente
1

Cuando no tiene ninguna línea #!(llamada shebang ), se usa sh . Para verificar eso, puede ejecutar el siguiente script.

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

En mi computadora consigo

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

incluso si mi shell predeterminado es zsh . Utiliza bash ya que en mi máquina, bash implementa el comando sh .

rools
fuente
1
¿Por qué rechazaste eso? Por favor explique o es inútil ...
rools
Los que odian (downvoters anónimos silenciosos) odiarán. Aprendí algo de tu respuesta, así que aquí hay un voto positivo. :-)
Mac