Errore shebang di FreeBSD

Aug 22 2020

Vorrei mettere shebang #!/bin/sh -eufo pipefailnella mia sceneggiatura. Ma ci sono diverse cose strane:

  1. Lo script fallirebbe con quella frase in FreeBSD ma non se eseguito su MacOS
  2. su FreeBSd, lo stesso shebang funziona quando eseguito direttamente dalla riga di comando (anche /bin/sh).
>>> sh -eufo pipefail -c 'echo hi'  # this works
hi

>>> cat <<EOF > script                                                                                      
#!/bin/sh -eufo pipefail
echo hi
EOF

>>> chmod +x ./script 
>>> ./script  # this doesn't work on FreeBSD but works on MacOS
Illegal option -o ./script

>>> cat ./script 
#!/bin/sh -eufo pipefail
echo hi

>>> uname -a
FreeBS 11.3-RELEASE-p7

Risposte

1 JdeBP Aug 22 2020 at 20:11

MacOS mantiene ancora il vecchio comportamento di FreeBSD di prima del 2005. Nel 2005, c'è stato un cambiamento importante nel modo in cui il kernel di FreeBSD gestiva #!all'inizio di un file eseguibile passato a execve(), per renderlo più in linea con alcuni altri kernel del sistema operativo, inclusi Linux e il kernel NetBSD.

Il commento nel codice sorgente del kernel NetBSD cerca di dipingere questo come un universale:

* raccogliere l'argomento della shell. tutto dopo il nome della shell
 * viene passato come UN argomento; questo è il corretto (storico)
 * comportamento.
- kern/exec-script.c. NetBSD. righe 189 e segg ..

In realtà non lo è. Sven Mascheck ha eseguito alcuni test circa un decennio fa e ci sono quattro comportamenti di base, quello AT&T Unix System 5 ha la stessa pretesa di essere un comportamento "storico corretto" quanto quello 4.2BSD:

  • Ignora i caratteri (prima di 4.2BSD e AT&T Unix System 5).
  • Passa l'intera stringa in un unico argomento (4.2BSD, NetBSD, Linux e FreeBSD dal 2005 in poi).
  • Dividi la stringa di spazi bianchi e passala come più argomenti (FreeBSD prima del 2005 e MacOS).
  • Dividi la stringa in spazi bianchi e passa solo il primo argomento (AT&T Unix System 5 e Solaris)

Ho incluso solo i sistemi operativi rilevanti per questa risposta tra parentesi. M. Mascheck ha controllato molto di più, come ha fatto Ahmon Dancy nella discussione di FreeBSD Problem Report 16393. Vedere la lettura successiva per gli elenchi completi.

Ciò che ha portato le cose alla testa in FreeBSD nel 2005 è stato che, ironicamente, FreeBSD non era così semplice. Era stata introdotta una modifica che aveva lo scopo di far funzionare effettivamente le cose scritte in libri popolari su Perl: gli argomenti venivano saltati dopo un carattere di commento. I libri avevano consigliato cose come:

#! / bin / sh - # - * - perl - * -
- Larry Wall, Tom Christiansen, Jon Orwant (2000). Programming Perl: 3a edizione . O'Reilly Media. ISBN 9780596000271. p. 488.

PR 16393 nel 2000 era un modo per far sì che il kernel gestisse script Perl eseguibili, scritti nel modo in cui Larry Wall aveva detto che avrebbero funzionato. Tuttavia, ha rotto altre cose e non ha funzionato completamente.

C'erano alcuni avanti e indietro su questo. Infine, nel 2005 il meccanismo per far funzionare l'idea di Larry Wall et al. È stato spostato dal kernel, che è stato fatto per funzionare in modo compatibile con Linux, NetBSD e 4.2BSD (piuttosto che Solaris e AT&T Unix System 5) e realizzato la responsabilità di sh.

Il comportamento dal 2005 è stato quindi che la shell ottiene tre argomenti, il secondo argomento è l'intera coda della #!riga, e invocare lo script direttamente con execve()è effettivamente lo stesso che invocare:

sh '-eufo pipefail' ./script

Dovrebbe essere abbastanza ovvio il motivo per cui la shell Almquist (che è ciò che shè su FreeBSD) pensa che ./scriptsia l'argomento opzione per l' -oopzione, e che tratta la pipefailparte come ulteriori opzioni di una sola lettera raccolte dietro -(che non ha intorno all'elaborazione ancora).

Un'alternativa ovvia è anche quella di avere set -o pipefailcome primo comando nello script, come sottolineato inhttps://unix.stackexchange.com/a/533418/5132per la shell Bourne Again . Questo è stato aggiunto solo alla shell di FreeBSD Almquist nel 2019, tuttavia, e quindi è disponibile solo nelle versioni molto recenti di FreeBSD. (La shell Debian Almquist non l'ha ancora aggiunta, a partire dal 2020.)

Ulteriore lettura

schily Aug 23 2020 at 13:53

Anche se il tuo #!utilizzo non è portabile perché utilizza più del semplice:

#!/bin/sh

o il singolo argomento comunemente supportato come in:

#!/bin/sh -oneflag

Il vero problema è che stai usando un'opzione non portatile -o pipefail.

Questa è un'opzione di ksh93e bashnon è supportata da altre shell.

Sfondo:

  • Su MacOS, /bin/shè bashche è stato compilato in un modo specifico (ad esempio per far funzionare le sequenze di escape echodi default) per renderlo conforme a POSIX. Poiché bash supporta pipefail(vedi sopra), funziona su MacOS.

  • Su FreeBSD, /bin/shè ashche non supporta pipefail.

Hai due possibili modi per andare:

  • Non usare set -o pipefail

  • Aspetta due anni e riprova. Molto probabilmente funzionerà, perché 10 mesi fa abbiamo deciso di aggiungere questa opzione al prossimo standard POSIX (numero 8), vederehttps://www.austingroupbugs.net/view.php?id=789e poiché il prossimo standard POSIX sarà pubblicato in ca. un anno, v'è una grande opportunità che FreeBSD aggiungerà il supporto per set -o pipefailal ashpiù presto.