Quale interprete shell esegue uno script senza shebang?


16

Supponiamo che la shell predefinita per il mio account sia zsh ma ho aperto il terminale e avviato bash ed eseguito uno script chiamato prac002.sh, quale interprete di shell verrebbe utilizzato per eseguire lo script, zsh o bash? Considera il seguente esempio:

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?

** EDIT: ** Ecco il contenuto dello script

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname

Cosa dice la sceneggiatura in alto?
Michael Homer,

@MichaelHomer: niente. Ho appena modificato la domanda per aggiungere il contenuto dello script. Lo script ha il permesso eseguibile.
7_R3X

Se si desidera utilizzare la shell corrente, dot source, come in . prac002.sh, supponendo che lo script sia nella directory corrente.
thararpy,

1
Esegui . ./prac002.she verrà eseguito con la shell corrente, ovvero punto ( .), spazio () seguito dal percorso dello script. Si chiama dot-sourcing della tua sceneggiatura. ;-)
thararpy il

Risposte:


18

Poiché lo script non inizia con una #!riga shebang che indica quale interprete utilizzare, POSIX afferma che :

Se la execl()funzione fallisce a causa di un errore equivalente all'errore [ENOEXEC] definito nel volume Interfacce di sistema di POSIX.1-2008, la shell deve eseguire un comando equivalente a una chiamata invocata con il nome del percorso risultante dalla ricerca come primo operando , con tutti gli argomenti rimanenti passati alla nuova shell, tranne per il fatto che il valore di "$ 0" nella nuova shell può essere impostato sul nome del comando. Se il file eseguibile non è un file di testo, la shell potrebbe ignorare l'esecuzione di questo comando. In questo caso, deve scrivere un messaggio di errore e deve restituire uno stato di uscita di 126.

Quel fraseggio è un po 'ambiguo, e conchiglie diverse hanno interpretazioni diverse.

In questo caso, Bash eseguirà lo script usando se stesso . D'altra parte, se invece lo avessi eseguito da zsh, invece zsh userebbesh (qualunque cosa sia sul tuo sistema).

È possibile verificare quel comportamento per questo caso aggiungendo queste righe allo script:

echo $BASH_VERSION
echo $ZSH_VERSION

Noterai che, da Bash, la prima riga genera la tua versione, mentre la seconda non dice mai nulla, indipendentemente dalla shell che usi.

  • Se il tuo /bin/shè, diciamo, dashallora nessuna delle due righe produrrà nulla quando lo script verrà eseguito da zsh o dash.
  • Se il tuo /bin/shè un link a Bash, vedrai l'output della prima riga in tutti i casi.
  • Se /bin/shè una versione di Bash diversa da quella che stavi usando direttamente, vedrai un output diverso quando esegui lo script direttamente da bash e da zsh.

Il ps -p $$comando dalla risposta di rools mostrerà anche informazioni utili sul comando usato dalla shell per eseguire lo script.


Ho effettuato l'upgrade, tuttavia, utilizza DEFAULT SHELL se non viene specificato shebang, qualunque sia la shell predefinita specificata. Questo è il motivo per cui dovresti sempre, imho, specificare uno shebang.
thararpy,

4
Non è così, che è stato davvero l'intero punto della risposta. La tua seconda frase è certamente vera.
Michael Homer,

hai ragione, bash lo esegue usando se stesso, la shell predefinita sulla maggior parte delle mie scatole sembra essere ... bash, da lì la mia confusione. Ho appena provato su Solaris 8 con ksh come shell predefinita, abbastanza sicuro, bash lo esegue come bash ... ho imparato qualcosa di nuovo oggi, grazie (votato!) ;-)
thararpy

Grazie. Il fallimento della execl()chiamata si verifica quando uno script di shell non contiene uno shebang e viene eseguito come scriptname? Non succede quando uno script di shell viene eseguito come bash scriptname? Non succede quando uno script di shell contiene uno shebang e viene eseguito come scriptname?
StackExchange per tutto il


7

Poiché il file non appartiene a nessuno dei tipi di file eseguibili riconosciuti dal sistema e supponendo che si disponga dell'autorizzazione per eseguire quel file, la execve()chiamata di sistema in genere fallisce con un errore ENOEXEC( non un file eseguibile ).

Ciò che accade quindi dipende dall'applicazione e / o dalla funzione di libreria utilizzata per eseguire il comando.

Può essere ad esempio una shell, la funzione execlp()/ execvp()libc.

La maggior parte delle altre applicazioni utilizzerà una di quelle quando eseguono un comando. Invoceranno una shell ad esempio tramite la system("command line")funzione libc che in genere invocherà shl'analisi di quella riga di comando (il cui percorso può essere determinato al momento della compilazione (come /bin/shvs /usr/xpg4/bin/shsu Solaris)), o invocherà la shell memorizzata $SHELLda loro stessi come vicon il suo !comando o xterm -e 'command line'molti altri comandi ( su user -cinvocherà la shell di accesso dell'utente invece di $SHELL).

Generalmente, un file di testo privo di shebang che non inizia con #viene considerato come uno shscript. Quale shè varierà comunque.

execlp()/ execvp(), al execve()ritorno ENOEXECin genere invocherà shsu di esso. Per i sistemi che ne hanno più di uno shperché possono conformarsi a più di uno standard, il che shè generalmente determinato al momento della compilazione (dell'applicazione che utilizza execvp()/ execlp()collegando un diverso BLOB di codice che fa riferimento a un percorso diverso sh). Ad esempio, su Solaris, sarà /usr/xpg4/bin/sh(uno standard, POSIX sh) o /bin/sh(la shell Bourne (una shell antiquata) su Solaris 10 e precedenti, ksh93 in Solaris 11).

Quando si tratta di conchiglie, ci sono molte varianti. bash, AT&T ksh, la shell Bourne in genere interpreterà lo script stesso (in un processo figlio a meno che non execvenga utilizzato) dopo aver simulato un execve(), che non ha impostato tutte le variabili non esportate, chiuso tutte le fds close-on-exec, rimosso tutte le trap personalizzate, alias, funzioni ... ( bashinterpreterà lo script in shmodalità). yashsi eseguirà (con shas argv[0]so in shmode) per interpretarlo.

zsh, pdksh, ashConchiglie basati in genere richiamare sh(il percorso, determinato al momento della compilazione).

Per cshe tcsh(e shper alcuni dei primi BSD), se il primo carattere del file è #, allora si eseguiranno per interpretarlo, e in shaltro modo. Questo risale a un periodo pre-shebang in cui cshriconosceva #come commenti ma non la shell Bourne, quindi #c'era un indizio che si trattasse di uno script csh.

fish(almeno versione 2.4.0), restituisce solo un errore se execve()fallisce (non tenta di trattarlo come uno script).

Alcune shell (come basho AT&T ksh) proveranno prima a stabilire in modo euristico se il file è probabilmente destinato a essere uno script o meno. Quindi potresti scoprire che alcune shell si rifiuteranno di eseguire uno script se nei primi byte ha un carattere NUL.

Nota anche che se execve()fallisce con ENOEXEC ma il file ha una linea shebang, alcune shell cercano di interpretare quella linea shebang.

Quindi alcuni esempi:

  • Quando $SHELLè /bin/bash, xterm -e 'myscript with args'sarà myscriptinterpretato da bashin shmodalità. Mentre con xterm -e myscript with args, xtermutilizzerà execvp()così lo script verrà interpretato da sh.
  • su -c myscriptsu Solaris 10 dove rootsi trova la shell di login /bin/shed /bin/shè la shell Bourne sarà myscriptinterpretata dalla shell Bourne.
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'su Solaris 10 verrà interpretato da /usr/xpg4/bin/sh(stesso per /usr/xpg4/bin/env myscript).
  • find . -prune -exec myscript {} \;su Solaris 10 (utilizzo execvp()) verrà interpretato /bin/shanche con /usr/xpg4/bin/find, anche in un ambiente POSIX (un bug di conformità).
  • csh -c myscriptlo interpreterà cshse inizia con #, shaltrimenti.

Tutto sommato, non puoi essere sicuro di quale shell verrà utilizzata per interpretare quello script se non sai come e da cosa verrà invocato.

In ogni caso, read -pè bashsolo sintassi, quindi ti consigliamo di assicurarti che lo script sia interpretato da bash(ed evita quell'estensione fuorviante .sh). O conosci il percorso bashdell'eseguibile e usi:

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

Oppure puoi provare a fare affidamento su una $PATHricerca bashdell'eseguibile (supponendo che bashsia installato) usando:

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

(si envtrova quasi ovunque /usr/bin). In alternativa, è possibile renderlo compatibile POSIX + Bourne, nel qual caso è possibile utilizzarlo /bin/sh. Tutti i sistemi avranno a /bin/sh. Sulla maggior parte di essi sarà (per la maggior parte) compatibile POSIX, ma potresti comunque trovare una shell Bourne ogni tanto.

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"

1

Quando non hai alcuna linea #!(chiamata shebang ), viene usato sh . Per verificarlo, puoi eseguire il seguente script.

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

Sul mio computer ottengo

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

anche se la mia shell predefinita è zsh . Usa bash poiché sulla mia macchina, il comando sh è implementato da bash .


1
Perché l'hai votato? Per favore, spiegalo o è inutile ...
Rools

Gli odiatori (downvoter anonimi silenziosi) odieranno. Ho imparato qualcosa dalla tua risposta, quindi ecco un voto. :-)
Mac
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.