Come funziona questo shebang che inizia con un doppio trattino (-)?


14

Ho trovato il seguente tipo di shebang nella pagina RosettaCode:

--() { :; }; exec db2 -txf "$0"

Funziona con Db2 e una cosa simile per Postgres. Tuttavia, non capisco l'intera linea.

So che il doppio trattino è un commento in SQL, e successivamente chiama l'eseguibile Db2 con alcuni parametri che passano il file stesso come file. Ma che dire della parentesi, delle parentesi graffe, del colon e del punto e virgola e di come sostituire un vero shebang #! ?

https://rosettacode.org/wiki/Multiline_shebang#PostgreSQL

Risposte:


18

Correlati: quale interprete shell esegue uno script senza shebang?

Lo script non ha un shebang / hashbang / #!line, semplicemente perché non c'è un doppio trattino #!.

Tuttavia, lo script verrà eseguito da una shell (vedi sopra domande e risposte collegate) e in quella shell, se -è un carattere valido nel nome di una funzione, la riga dichiara una funzione di shell chiamata --che non fa nulla (beh, viene eseguita :, che non fa nulla ) e che non viene mai chiamato.

La funzione, nella più comune notazione multilinea (solo per rendere più ovvio come appare, dato che il suo strano nome in qualche modo oscura il fatto che in realtà è una funzione):

-- () {
  :
}

L'unico scopo della definizione della funzione è di avere una riga valida in uno script di shell e allo stesso tempo un comando SQL valido (un commento). Questo tipo di codice è chiamato poliglotta .

Dopo aver dichiarato la funzione di shell fasulla, lo script, quando eseguito da un interprete di script di shell, utilizza execper sostituire la shell corrente con il processo risultante dall'esecuzione db2 -txf "$0", che sarebbe lo stesso che usare db2 -txfsul percorso dello script dalla riga di comando.

Questo trucco probabilmente non funzionerebbe in modo affidabile su sistemi in cui dasho con ashshell basate su altri yash, la shell Bourne, ksh88o ksh93viene utilizzato come /bin/sh, poiché queste shell non accettano funzioni il cui nome contiene trattini.

Correlati anche:


Suppongo che funzionerebbe anche quanto segue (non realmente testato):

--() { exec db2 -txf "$0"; }; --

@ilkkachu Meglio adesso?
Kusalananda

1
Oh si! E grazie per avermi ricordato come si chiama quel genere di cose. :)
ilkkachu,

6

Come ha già detto @Kusalananda, quel trucco è rotto e non funzionerà in tutte le shell.

Ecco la mia idea di farlo in modo portabile:

--/.. 2>/dev/null; exec db2 -txf "$0"

Il primo comando dovrebbe fallire anche se --esiste un file / directory chiamato nella directory corrente e qualsiasi errore verrà chiuso da 2>/dev/null; la shell procederà quindi con il secondo comando, il exec.


Non è ancora molto portatile. Non è uno script valido e stai ancora facendo affidamento sulla shell chiamante per aggirare il fatto che il kernel si rifiuterà di eseguire lo script e tornerà ENOEXECse ci provi. Prova a eseguire lo script sotto straceper vedere cosa intendo.
Kasperd,

@kasperd, dovrebbe essere comunque portatile, la shell dovrebbe eseguire lo script come script di shell se exec()non funziona su di esso. "Se la funzione execl () fallisce a causa di un errore equivalente all'errore [ENOEXEC], la shell eseguirà un comando equivalente a far invocare una shell con il nome del comando come primo operando, ..." (vedi pubs.opengroup .org / onlinepubs / 9699919799.2018edition / utilities / ... )
ilkkachu

@ilkkachu Ma gli script non sono sempre eseguiti da una shell. Se si tenta di utilizzare lo script in qualsiasi altro contesto in cui un file eseguibile funzionerebbe, fallirà. Inoltre le shell non concordano su quale interprete usare. Quindi il tuo script ora si comporterà in modo diverso o fallirà del tutto a seconda del contesto da cui viene chiamato.
Kasperd,

@kasperd, beh, certo, non funzionerà se lo fai exec()direttamente da qualcosa di diverso da una shell. Ma quale sarebbe questo caso? Potresti voler eseguire lo script da crono simili, ma penso che esegua comunque tutto attraverso una shell e, in caso contrario, è facile spiegarlo db2 -txf /path/to/scriptin quel caso, dal momento che devi farlo solo una volta. Avere il lavoro di stenografia è principalmente utile su una shell interattiva. Ma certo, uno script wrapper separato potrebbe essere più robusto.
ilkkachu,

1
@kasperd Non ti annoierò con documenti e standard; provalo! echo 'int main(int c,char**a){execvp(a[1],a+1);}' | cc -include unistd.h -xc -; echo echo yeah > a.sh; chmod 755 a.sh; ./a.out ./a.sh; PATH=`pwd` ./a.out a.sh
Zio Billy,
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.