en-têtes de script shell (#! / bin / sh vs #! / bin / csh)

92

Pourquoi tous les fichiers de script commencent-ils par

#!/bin/sh

ou avec

#!/bin/csh

Est-ce nécessaire? Quel est le but de cela? Et quelle est la différence entre les deux?

Un deux trois
la source
1
Pour un script csh, vous devez utiliser #!/bin/csh -f; le -fdit au shell de ne pas rechercher l'utilisateur .loginet .cshrc, ce qui accélère l'exécution du script et évite les dépendances sur la configuration de l'utilisateur. (Ou mieux encore, n'écrivez pas de scripts csh.) Ne l'utilisez pas -fpour les scripts sh ou bash; cela n'a pas la même signification.
Keith Thompson

Réponses:

99

Ceci est connu comme un Shebang:

http://en.wikipedia.org/wiki/Shebang_(Unix)

#! interpréteur [arg-facultatif]

Un shebang n'est pertinent que lorsqu'un script a l'autorisation d'exécution (par exemple chmod u + x script.sh).

Lorsqu'un shell exécute le script, il utilise l'interpréteur spécifié.

Exemple:

#!/bin/bash
# file: foo.sh
echo 1

$ chmod u+x foo.sh
$ ./foo.sh
  1
Kwarrick
la source
@Kolob Canyon, vous n'avez pas besoin de le faire, mais cela peut aider certains éditeurs avec la coloration syntaxique (bien qu'il existe généralement d'autres moyens pour obtenir la même chose): unix.stackexchange.com/a/88730/193985
Braham Snyder
42

La #!ligne indique au noyau (spécifiquement, l'implémentation de l' execveappel système) que ce programme est écrit dans un langage interprété; le chemin absolu qui suit identifie l'interpréteur. Les programmes compilés en code machine commencent par une séquence d'octets différente - sur la plupart des Unix modernes, 7f 45 4c 46( ^?ELF) qui les identifie comme tels.

Vous pouvez placer un chemin absolu vers n'importe quel programme de votre choix après le #!, tant que ce programme n'est pas lui-même un #!script. Le noyau réécrit une invocation de

./script arg1 arg2 arg3 ...

./scriptcommence par, disons, #! /usr/bin/perlcomme si la ligne de commande avait effectivement été

/usr/bin/perl ./script arg1 arg2 arg3

Ou, comme vous l'avez vu, vous pouvez utiliser #! /bin/shpour écrire un script destiné à être interprété par sh.

La #!ligne n'est traitée que si vous appelez directement le script ( ./scriptsur la ligne de commande); le fichier doit également être exécutable ( chmod +x script). Si vous faites, sh ./scriptla #!ligne n'est pas nécessaire (et sera ignorée si elle est présente), et le fichier n'a pas besoin d'être exécutable. Le but de cette fonctionnalité est de vous permettre d'appeler directement des programmes en langage interprété sans avoir à savoir dans quelle langue ils sont écrits. (Faites grep '^#!' /usr/bin/*- vous découvrirez qu'un grand nombre de programmes de base utilisent en fait cette fonctionnalité.)

Voici quelques règles d'utilisation de cette fonctionnalité:

  • Le #!doit être les deux premiers octets du fichier. En particulier, le fichier doit être dans un encodage compatible ASCII (par exemple, UTF-8 fonctionnera, mais UTF-16 ne le fera pas) et ne doit pas commencer par une "marque d'ordre d'octet" , sinon le noyau ne le reconnaîtra pas comme un #!scénario.
  • Le chemin après #!doit être un chemin absolu (commence par /). Il ne peut pas contenir de caractères d'espace, de tabulation ou de nouvelle ligne.
  • Il est judicieux, mais pas obligatoire, de mettre un espace entre le #!et le /. N'y mettez pas plus d'un espace.
  • Vous ne pouvez pas mettre de variables shell sur la #!ligne, elles ne seront pas développées.
  • Vous pouvez placer un argument de ligne de commande après le chemin absolu, séparé de celui-ci par un seul espace. Comme le chemin absolu, cet argument ne peut pas contenir de caractères d'espace, de tabulation ou de nouvelle ligne. Parfois, c'est nécessaire pour faire fonctionner les choses ( #! /usr/bin/awk -f), parfois c'est juste utile ( #! /usr/bin/perl -Tw). Malheureusement, vous ne pouvez pas mettre deux arguments ou plus après le chemin absolu.
  • Certaines personnes vous diront d'utiliser à la #! /usr/bin/env interpreterplace de #! /absolute/path/to/interpreter. C'est presque toujours une erreur. Cela rend le comportement de votre programme dépendant de la $PATHvariable de l'utilisateur qui appelle le script. Et tous les systèmes ne l'ont pas fait enven premier lieu.
  • Les programmes qui ont besoin setuidou les setgidprivilèges ne peuvent pas utiliser #!; ils doivent être compilés en code machine. (Si vous ne savez pas ce que setuidc'est, ne vous inquiétez pas.)

En ce qui concerne csh, cela se rapporte à shpeu près à ce que le substitut de thé avancé Nutrimat fait au thé. Il a (ou plutôt eu; les implémentations modernes shont rattrapé) un certain nombre d'avantages par rapport shà une utilisation interactive, mais l'utiliser (ou son descendant tcsh) pour le script est presque toujours une erreur . Si vous êtes nouveau dans le script shell en général, je vous recommande fortement de l'ignorer et de vous concentrer sur sh. Si vous utilisez un cshparent comme shell de connexion, basculez vers bashou zsh, afin que le langage de commande interactif soit le même que le langage de script que vous apprenez.

zwol
la source
Les versions récentes de Linux permettent à l'interpréteur spécifié d'être un script. Il est courant d'omettre l'espace après le #!; aucun commentaire pour savoir si c'est du bon style. Voir cette question et ma réponse pour une discussion sur les avantages et les inconvénients du #!/usr/bin/envhack.
Keith Thompson
@KeithThompson J'ai l'impression que Linux est la seule variante Unix courante qui permet à l'interpréteur d'être un script, donc ce n'est toujours pas quelque chose sur lequel s'appuyer. Depuis que j'ai écrit ceci, j'ai moi-même rencontré une situation où #!/usr/bin/envétait la bonne chose, mais je reste d'avis que c'est presque toujours une mauvaise idée.
zwol
4

Ceci définit le shell (interpréteur de commandes) que vous utilisez pour interpréter / exécuter votre script. Chaque shell est légèrement différent dans la façon dont il interagit avec l'utilisateur et exécute des scripts (programmes).

Lorsque vous tapez une commande à l'invite Unix, vous interagissez avec le shell.

Par exemple, #!/bin/cshfait référence au C-shell, /bin/tcshau t-shell, /bin/bashau bash shell, etc.

Vous pouvez dire quel shell interactif vous utilisez le

 echo $SHELL

commande, ou alternativement

 env | grep -i shell

Vous pouvez changer votre shell de commande avec la chshcommande.

Chacun a un jeu de commandes légèrement différent et une manière d'affecter des variables et son propre ensemble de constructions de programmation. Par exemple, l'instruction if-else avec bash est différente de celle du C-shell.

Cette page pourrait être intéressante car elle "traduit" entre les commandes / syntaxe bash et tcsh.

L'utilisation de la directive dans le script shell vous permet d'exécuter des programmes en utilisant un shell différent. Par exemple, j'utilise le tcshshell de manière interactive, mais j'exécute souvent des scripts bash en utilisant / bin / bash dans le fichier de script.

De côté:

Ce concept s'étend également à d'autres scripts. Par exemple, si vous programmez en Python, vous mettriez

 #!/usr/bin/python

en haut de votre programme Python

Levon
la source
Alors, est-ce nécessaire? Comment savoir quel shell j'utilise réellement?
One Two Three
Donc, si j'écris les scripts pour quelqu'un à utiliser sur leur machine, et je ne sais pas quel shell ils utilisent. (Cette personne, malheureusement, n'a aucune idée de ce genre de choses, donc tout ce qu'elle peut faire est d'exécuter le script sans rien changer). Puis-je faire quelque chose comme #! $SHELL? Est-ce que cela mettrait la bonne coquille dans le Shebang?
One Two Three
1
@OneTwoThree La plupart des systèmes ont les shells standard, si vous écrivez un script bash ou csh, tout ira bien. Le shell qu'ils utilisent de manière interactive n'a pas d' importance, c'est la beauté de la !#/bin/bashdirective par exemple . Il indique au système quel shell utiliser pour exécuter votre script shell.
Levon
La valeur de $SHELLne vous dit pas nécessairement quel shell vous utilisez actuellement; il vous indique normalement votre shell par défaut . ensembles tcsh $versionet $tcsh; ensembles bash $BASH_VERSION. Tous les obus n'ont pas nécessairement des mécanismes similaires.
Keith Thompson
1
@OneTwoThree: La #!ligne doit correspondre à la syntaxe du script, pas au shell interactif utilisé par celui qui exécute le script.
Keith Thompson