Perché il "-" nel "#! / bin / sh - "shebang?


28
#! / bin / sh -

È (o almeno era) spesso lo shebang raccomandato per far interpretare una sceneggiatura /bin/sh.

Perché non solo #! /bin/sho #!/bin/sh?

A cosa serve -?

Risposte:


38

Questo è per un motivo simile al motivo per cui è necessario scrivere:

rm -- *.txt

E non

rm *.txt

A meno che non sia possibile garantire che nessuno dei .txtfile nella directory corrente abbia un nome che inizia con -.

Nel:

rm <arg>

<arg>è considerata un'opzione se inizia con -o un file da rimuovere in altro modo. Nel

rm - <arg>

argviene sempre considerato come un file da rimuovere indipendentemente dal fatto che inizi -o meno.

È lo stesso per sh.

Quando si esegue uno script che inizia con

#! /bin/sh

In genere con:

execve("path/to/the-script", ["the-script", "arg"], [environ])

Il sistema lo trasforma in:

execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])

Di path/to/the-scriptsolito non è qualcosa che è sotto il controllo dell'autore dello script. L'autore non può prevedere dove verranno archiviate le copie dello script né con quale nome. In particolare, non possono garantire che il nome path/to/the-scriptcon cui viene chiamato non inizi -(o +che è anche un problema sh). Ecco perché abbiamo bisogno del -qui per segnare la fine delle opzioni.

Ad esempio, sul mio sistema zcat(come la maggior parte degli altri script in realtà) c'è un esempio di uno script che non ha seguito quella raccomandazione:

$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]

Ora potresti chiedere perché #! /bin/sh -e no #! /bin/sh --?

Mentre #! /bin/sh --funzionerebbe con le shell POSIX, #! /bin/sh -è più portatile; in particolare alle versioni antiche di sh. shtrattare -come e precede getopt()la fine dell'opzione e l'uso generale di --per contrassegnare la fine delle opzioni da molto tempo. Il modo in cui la shell Bourne (dalla fine degli anni '70) analizzava i suoi argomenti, solo il primo argomento veniva preso in considerazione per le opzioni se fosse iniziato -. Tutti i personaggi dopo -verrebbero trattati come nomi di opzioni; se non c'erano personaggi dopo il -, non c'erano opzioni. Quelle conchiglie bloccate e in seguito simili a quelle di Bourne riconoscono -come un modo per segnare la fine delle opzioni.

Nella shell Bourne (ma non nelle moderne shell simili a Bourne), #! /bin/sh -eufaggirerebbe anche il problema in quanto solo il primo argomento è stato preso in considerazione per le opzioni.

Ora, si potrebbe dire che siamo pedanti qui, ed è anche il motivo per cui ho scritto la necessità in corsivo sopra:

  1. nessuno nella loro mente corretta chiamerebbe uno script con qualcosa che inizia con -o +o li inserisce in una directory il cui nome inizia con -o +.
  2. anche se lo facessero, in primo luogo si potrebbe obiettare che possono solo incolpare se stessi, ma anche, quando si invoca uno script, il più delle volte, viene da una shell o dalle funzioni execvp()/ execlp()-type. E in quel caso, in genere li invochi sia perché the-scriptvenga cercato $PATHnel qual caso l'argomento percorso alla execve()chiamata di sistema in genere inizierà con /(non -nor +) che come ./the-scriptse si desideri the-scripteseguire nella directory corrente (e quindi il percorso inizia con ./, -né con +nessuno dei due).

Ora oltre al problema della correttezza teorica , c'è un'altra ragione per cui è #! /bin/sh -stata raccomandata come buona pratica . E questo risale al tempo in cui diversi sistemi supportavano ancora gli script setuid.

Se hai uno script che contiene:

#! /bin/sh
/bin/echo "I'm running as root"

E quello script era setuid root (come con i -r-sr-xr-x root binpermessi), su quei sistemi, quando eseguito da un normale utente, il

execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])

sarebbe fatto come root!

Se l'utente creasse un collegamento simbolico /tmp/-i -> path/to/the-scripte lo eseguisse come -i, allora avvierebbe una shell interattiva ( /bin/sh -i) come root.

Il -funzionerebbe in giro che (non avrebbe funzionato intorno alla questione della razza-condizioni , o il fatto che alcune shimplementazioni, come alcuni ksh88quelli basati avrebbero occhiata argomenti script senza /in $PATHperò).

Al giorno d'oggi, quasi nessun sistema supporta più lo script setuid e alcuni di quelli che ancora lo fanno (di solito non di default), finiscono per fare un execve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])(dove <n>è un descrittore di file aperto per la lettura sullo script) che aggira sia questo problema che il condizione di gara.

Si noti che si verificano problemi simili con la maggior parte degli interpreti, non solo con shell tipo Bourne. Le shell non simili a Bourne generalmente non supportano -come marker di fine opzione, ma generalmente supportano --invece (almeno per le versioni moderne).

Anche

#! /usr/bin/awk -f
#! /usr/bin/sed -f

Non avere il problema poiché l'argomento successivo è considerato come argomento -fdell'opzione in ogni caso, ma non funziona ancora se path/to/scriptè -(nel qual caso, con la maggior parte sed/ awkimplementazioni, sed/ awknon leggere il codice dal -file, ma da stdin invece).

Inoltre, sulla maggior parte dei sistemi, non è possibile utilizzare:

#! /usr/bin/env sh -
#! /usr/bin/perl -w --

Come nella maggior parte dei sistemi, il meccanismo shebang consente solo un argomento dopo il percorso dell'interprete.

Per quanto riguarda se usare #! /bin/sh -vs #!/bin/sh -, questa è solo una questione di gusti. Preferisco il primo in quanto rende più visibile il percorso dell'interprete e facilita la selezione del mouse. C'è una leggenda che dice che lo spazio era necessario in alcune antiche versioni di Unix ma AFAIK, che non è mai stato verificato.

Un ottimo riferimento alla Shebang Unix può essere trovato su https://www.in-ulm.de/~mascheck/various/shebang


1
Questo ha qualche rilevanza con #! / Bin / bash?
Joe,

1
@Joe, sì, certo, bashaccetta opzioni come ogni altra shell. Essendo una shell tipo Bourne e POSIX, bashaccetta sia #! /bin/bash -e #! /bin/bash --.
Stéphane Chazelas,
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.