Por que o "-" no "#!" / bin / sh - ”shebang?

27
#! / bin / sh -

É (ou pelo menos foi) geralmente o shebang recomendado para interpretar um script /bin/sh.

Por que não apenas #! /bin/shou #!/bin/sh?

Para que é isso -?

Stéphane Chazelas
fonte

Respostas:

37

É por um motivo semelhante ao por que você precisa escrever:

rm -- *.txt

E não

rm *.txt

A menos que você possa garantir que nenhum dos .txtarquivos no diretório atual tenha um nome que comece com -.

Em:

rm <arg>

<arg>é considerado uma opção se iniciar com -ou um arquivo para remover de outra forma. Em

rm - <arg>

argé sempre considerado como um arquivo a ser removido, independentemente de iniciar -ou não.

É o mesmo para sh.

Quando alguém executa um script que começa com

#! /bin/sh

Normalmente com:

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

O sistema o transforma em:

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

A path/to/the-scriptgeralmente não é algo que está sob o controle do autor script. O autor não pode prever onde as cópias do script serão armazenadas nem com que nome. Em particular, eles não podem garantir que o modo path/to/the-scriptcomo é chamado não comece -(ou +que também seja um problema sh). É por isso que precisamos do -aqui para marcar o final das opções.

Por exemplo, no meu sistema zcat(como a maioria dos outros scripts, na verdade) é um exemplo de script que não seguiu essa recomendação:

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

Agora você pode perguntar por que #! /bin/sh -e não #! /bin/sh --?

Embora #! /bin/sh --funcionasse com shells POSIX, o #! /bin/sh -é mais portátil; em particular para versões antigas de sh. shtratar -como e o final da opção é anterior getopt()e o uso geral de --para marcar o final das opções por um longo tempo. Da maneira como o shell Bourne (do final dos anos 70) analisou seus argumentos, apenas o primeiro argumento foi considerado para as opções se ele começou com ele -. Todos os caracteres a seguir -seriam tratados como nomes de opções; se não houvesse caracteres após o -, não havia opções. Isso ficou preso e todas as conchas semelhantes a Bourne mais tarde reconhecem -como uma maneira de marcar o fim das opções.

No shell Bourne (mas não nos modernos cascos Bourne), #! /bin/sh -euftambém contornaria o problema, pois apenas o primeiro argumento foi considerado para opções.

Agora, pode-se dizer que estamos sendo pedantes aqui, e também é por isso que escrevi a necessidade em itálico acima:

  1. ninguém em sã consciência chamaria um script com algo começando com -ou +ou os colocaria em um diretório cujo nome começa com -ou +.
  2. mesmo que o fizessem, o primeiro seria argumentar que eles só podem se culpar, mas também, quando você invoca um script, mais frequentemente do que não, é de um shell ou de funções execvp()/ / execlp()-type. E, nesse caso, geralmente você os chama, the-scriptpara que seja procurado; $PATHnesse caso, o argumento do caminho para a execve()chamada do sistema geralmente começa com /(não -nem +) ou como ./the-scriptse você deseja que the-scripto diretório atual seja executado (e então o caminho começa com ./, -nem +nem).

Agora, além do problema de correção teórica , há outra razão pela qual #! /bin/sh -foi recomendada como boa prática . E isso remonta a uma época em que vários sistemas ainda suportavam scripts setuid.

Se você possui um script que contém:

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

E esse script foi definido como root (como com -r-sr-xr-x root binpermissões), nesses sistemas, quando executado por um usuário comum, o

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

seria feito como root!

Se o usuário criou um link simbólico /tmp/-i -> path/to/the-scripte o executou como -i, ele iniciaria um shell interativo ( /bin/sh -i) como root.

O -iria trabalhar em torno disso (que não iria funcionar em torno da questão racial-condição , ou o fato de que algumas shimplementações como alguns ksh88queridos baseados iria pesquisar argumentos de script, sem /no $PATHentanto).

Atualmente, quase nenhum sistema suporta script setuid por mais tempo, e alguns dos que ainda o fazem (geralmente não o padrão) acabam executando um execve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])(onde <n>existe um descritor de arquivo aberto para leitura no script) que funciona em torno desse problema e do condição de corrida.

Observe que você obtém problemas semelhantes com a maioria dos intérpretes, não apenas com os cascos Bourne. Os shells não semelhantes a Bourne geralmente não suportam -como marcador de final de opção, mas geralmente suportam --(pelo menos para versões modernas).

Além disso

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

Não tenha o problema, pois o próximo argumento é considerado como um argumento para a -fopção em qualquer caso, mas ainda não funcionará se path/to/scriptestiver -(nesse caso, com a maioria das sed/ awkimplementações, sed/ awknão leia o código no -arquivo, mas do stdin).

Observe também que na maioria dos sistemas, não se pode usar:

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

Como na maioria dos sistemas, o mecanismo shebang permite apenas um argumento após o caminho do intérprete.

Quanto ao uso de #! /bin/sh -vs #!/bin/sh -, isso é apenas uma questão de gosto. Prefiro o primeiro, pois torna o caminho do intérprete mais visível e facilita a seleção do mouse. Há uma lenda que diz que o espaço era necessário em algumas versões antigas do Unix, exceto o AFAIK, que nunca foi verificado.

Uma referência muito boa sobre o shebang do Unix pode ser encontrada em https://www.in-ulm.de/~mascheck/various/shebang

Stéphane Chazelas
fonte
11
Isso tem alguma relevância para #! / Bin / bash?
18717 Joe
11
@ Joe, sim, é claro, bashaceita opções como qualquer outro shell. Sendo um shell Bourix e POSIX, bashaceita ambos #! /bin/bash -e #! /bin/bash --.
Stéphane Chazelas 19/03/19