Pourquoi le «-» dans le «#! / bin / sh - ”shebang?

28
#! / bin / sh -

Est (ou du moins était) souvent le shebang recommandé pour interpréter un script /bin/sh.

Pourquoi pas juste #! /bin/shou #!/bin/sh?

À quoi ça -sert?

Stéphane Chazelas
la source

Réponses:

38

C'est pour une raison similaire à la raison pour laquelle vous devez écrire:

rm -- *.txt

Et pas

rm *.txt

Sauf si vous pouvez garantir qu'aucun des .txtfichiers du répertoire actuel ne porte un nom commençant par -.

Dans:

rm <arg>

<arg>est considéré comme une option s'il commence par -ou un fichier à supprimer dans le cas contraire. Dans

rm - <arg>

argest toujours considéré comme un fichier à supprimer, qu'il démarre -ou non.

C'est pareil pour sh.

Quand on exécute un script qui commence par

#! /bin/sh

Généralement avec:

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

Le système le transforme en:

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

Ce path/to/the-scriptn'est généralement pas quelque chose qui est sous le contrôle de l'auteur du script. L'auteur ne peut pas prédire où les copies du script seront stockées ni sous quel nom. En particulier, ils ne peuvent pas garantir que le nom path/to/the-scriptavec lequel il est appelé ne commencera pas -(ou +qui est également un problème avec sh). C'est pourquoi nous avons besoin de l' -ici pour marquer la fin des options.

Par exemple, sur mon système zcat(comme la plupart des autres scripts en fait) est un exemple de script qui n'a pas suivi cette recommandation:

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

Vous pouvez maintenant demander pourquoi #! /bin/sh -et non #! /bin/sh --?

Alors #! /bin/sh --qu'il fonctionnerait avec des shells POSIX, le #! /bin/sh -est plus portable; en particulier aux anciennes versions de sh. shle traitement des prédates -de fin d'option et de fin d'option getopt()et l'utilisation générale de --pour marquer la fin des options de longue date. La façon dont le shell Bourne (à partir de la fin des années 70) a analysé ses arguments, seul le premier argument était considéré pour les options s'il commençait par -. Tous les caractères suivants -seraient traités comme des noms d'options; s'il n'y avait pas de caractère après le -, il n'y avait pas d'options. Cela a bloqué et tous les obus similaires à Bourne plus tard reconnaissent -comme un moyen de marquer la fin des options.

Dans le shell Bourne (mais pas dans les shells modernes de type Bourne), #! /bin/sh -eufcontournerait également le problème car seul le premier argument était considéré pour les options.

Maintenant, on pourrait dire que nous sommes pédants ici, et c'est aussi pourquoi j'ai écrit le besoin en italique ci-dessus:

  1. personne sensé n'appelle un script avec quelque chose commençant par -ou +ou les place dans un répertoire dont le nom commence par -ou +.
  2. même s'ils l'ont fait, on dirait d'abord qu'ils ne peuvent que se blâmer, mais aussi, lorsque vous invoquez un script, le plus souvent, cela provient d'un shell ou de fonctions de type execvp()/ execlp(). Et dans ce cas, vous les invoquez généralement soit the-scriptpour qu'il soit recherché, $PATHauquel cas l'argument de chemin d'accès à l' execve()appel système commence généralement par /(pas -ni +), soit comme ./the-scriptsi vous vouliez que the-scriptle répertoire courant soit exécuté (et alors le chemin commence par ./, pas -non +plus).

Maintenant, à côté de la question de l' exactitude théorique , il y a une autre raison pour laquelle elle a #! /bin/sh -été recommandée comme bonne pratique . Et cela remonte à une époque où plusieurs systèmes prenaient encore en charge les scripts setuid.

Si vous avez un script qui contient:

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

Et ce script était root setuid (comme avec les -r-sr-xr-x root binautorisations), sur ces systèmes, lorsqu'il est exécuté par un utilisateur ordinaire, le

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

se ferait comme root!

Si l'utilisateur créait un lien symbolique /tmp/-i -> path/to/the-scriptet l'exécutait en tant que -i, il lancerait un shell interactif ( /bin/sh -i) en tant que root.

Le -travaillerait autour de cette (cela ne fonctionnerait pas autour de la question condition de course , ou le fait que certaines shmises en œuvre comme certains ksh88les auraient à base de script rechercher des arguments sans /en $PATHbien).

De nos jours, pratiquement aucun système ne prend plus en charge le script setuid, et certains de ceux qui le font encore (généralement pas par défaut), finissent par faire un execve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])(où <n>est un descripteur de fichier ouvert pour la lecture sur le script) qui contourne à la fois ce problème et le condition de course.

Notez que vous rencontrez des problèmes similaires avec la plupart des interprètes, pas seulement les shells de type Bourne. Les shells non Bourne-like ne prennent généralement pas en charge -comme marqueur de fin d'option, mais prennent généralement en charge à la --place (au moins pour les versions modernes).

Également

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

N'ayez pas le problème car l'argument suivant est considéré comme un argument de l' -foption dans tous les cas, mais cela ne fonctionne toujours pas si path/to/scriptest -(dans ce cas, avec la plupart des sed/ awkimplémentations, sed/ awkne lit pas le code de la -fichier, mais à partir de stdin à la place).

Notez également que sur la plupart des systèmes, on ne peut pas utiliser:

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

Comme sur la plupart des systèmes, le mécanisme shebang n'autorise qu'un seul argument après le chemin de l'interpréteur.

Quant à savoir si utiliser #! /bin/sh -vs #!/bin/sh -, c'est juste une question de goût. Je préfère le premier car il rend le chemin de l'interpréteur plus visible et facilite la sélection de la souris. Il y a une légende qui dit que l'espace était nécessaire dans certaines anciennes versions d'Unix mais AFAIK, qui n'a jamais été vérifié.

Une très bonne référence sur le shebang Unix peut être trouvée sur https://www.in-ulm.de/~mascheck/various/shebang

Stéphane Chazelas
la source
1
Est-ce que cela a un rapport avec #! / Bin / bash?
Joe
1
@Joe, oui bien sûr, bashaccepte des options comme tout autre shell. Étant un shell Bourne-like et POSIX, bashaccepte les deux #! /bin/bash -et #! /bin/bash --.
Stéphane Chazelas