Différentes façons d'exécuter un script shell


44

Il y a plusieurs façons d'exécuter un script, celles que je connais sont les suivantes:

/path/to/script # using the path (absolute or relative)
. script        # using the . (dot)
source script   # using the `source` command

Sont les plus de cela? Quelles sont les différences entre eux? Existe-t-il des situations dans lesquelles je dois utiliser l'une et pas l'autre?


Bon à savoir, grâce à votre question et à vos réponses ci-dessous, en particulier à Shawn. J'aimerais ajouter quelque chose qui n'était pas très évident pour moi jusqu'à ce que je fasse quelques tests. L'inclusion d'un "/" dans la deuxième manière ci-dessus amène la commande au mode 1 ci-dessus. C'est-à-dire que, bien que "./myscript.sh" suive le mode 1, ". Myscript.sh" reste en mode 2. Vous avez parlé de "utiliser le chemin (absolu ou relatif)", mais vous vouliez simplement que cela soit apparent.
Arun

Réponses:


32

Une autre méthode consiste à appeler l'interprète et à lui transmettre le chemin du script:

/bin/sh /path/to/script

Le point et la source sont équivalents. (EDIT: non, ils ne le sont pas: comme le souligne KeithB dans un commentaire sur une autre réponse, "." Ne fonctionne que dans les shells liés à bash, où "source" fonctionne dans les shells lié à bash et csh.) Il exécute le script dans -place (comme si vous avez copié et collé le script ici). Cela signifie que toutes les fonctions et variables non locales du script restent. Cela signifie également que si le script insère un cd dans un répertoire, vous serez toujours là quand ce sera fait.

Les autres méthodes d'exécution d'un script seront exécutées dans son propre sous-shell. Les variables dans le script ne sont pas encore en vie quand c'est fait. Si le script a changé de répertoire, cela n’affecte pas l’environnement d’appel.

/ path / to / script et / bin / sh Les scripts sont légèrement différents. Typiquement, un script a un "shebang" au début qui ressemble à ceci:

#! /bin/bash

C'est le chemin de l'interpréteur de script. S'il spécifie un interprète différent de celui que vous utilisez lorsque vous l'exécutez, il peut se comporter différemment (ou ne pas fonctionner du tout).

Par exemple, les scripts Perl et les scripts Ruby commencent par (respectivement):

#! /bin/perl

et

#! /bin/ruby

Si vous exécutez l'un de ces scripts en même temps /bin/sh script, ils ne fonctionneront pas du tout.

Ubuntu n’utilise pas le shell bash, mais un très similaire appelé dash. Les scripts qui nécessitent bash peuvent fonctionner légèrement mal lorsqu'ils sont appelés /bin/sh scriptcar vous venez d'appeler un script bash à l'aide de l'interpréteur de tirets.

Une autre petite différence entre l'appel direct du script et la transmission du chemin d'accès du script à l'interpréteur réside dans le fait que le script doit être marqué comme exécutable pour pouvoir l'exécuter directement, mais pas pour l'exécuter en transmettant le chemin d'accès à l'interpréteur.

Autre variante mineure: vous pouvez préfixer l’une de ces manières d’exécuter un script avec eval, vous pouvez donc avoir

eval sh script
eval script
eval . script

etc. Cela ne change rien, mais je pensais l'inclure pour plus de précision.


6
Dire "Ubuntu n'utilise pas réellement le shell bash" est imprécis et techniquement incorrect. Ubuntu - t utiliser le shell bash, le point est que shcorrespond à dash, mais pas bash.
Faheem Mitha

@Shawn Dans le premier paragraphe, vous avez écrit "Il exécute le script sur place (comme si vous l'aviez copié et collé ici.). Cela signifie que toutes les fonctions et les variables non locales du script restent." qu'entendez-vous par la deuxième ligne ici? Pouvez-vous s'il vous plaît expliquer.
Geek

@ Geek lorsque vous exécutez un script en tant que processus enfant (de manière normale), toutes les variables et fonctions qu'il définit (son environnement) disparaissent à la fin du processus. Si vous sourcez le script, ces variables et fonctions sont créées dans l'environnement actuel. une fois le script terminé, les modifications apportées à l'environnement restent.
Shawn J. Goff

@ ShawnJ.Goff merci pour la clarification. +1
Geek

Tour de rôle: Bourne Shell (sh) accepte uniquement point - pas la source car il est intégré à bash. pubs.opengroup.org/onlinepubs/9699919799/utilities/… Je dirais donc que le moyen le plus portable est un point.
dezza

9

La plupart des gens déboguent des scripts shell en ajoutant les indicateurs de débogage suivants au script:

set -x     # Print command traces before executing command.
set -v     # Prints shell input lines as they are read.
set -xv    # Or do both

Mais cela signifie que vous devez ouvrir le fichier avec un éditeur (en supposant que vous ayez le droit de modifier le fichier), en ajoutant une ligne comme set -x, enregistrez le fichier, puis exécutez le fichier. Ensuite, lorsque vous avez terminé, vous devez suivre les mêmes étapes et supprimer les éléments set -x, etc., etc. Cela peut être fastidieux.

Au lieu de faire tout cela, vous pouvez définir les indicateurs de débogage sur la ligne de commande:

$ bash -x ~/bin/ducks
+ du -cks -x dir1 dir2 dir3 file1 file2 file3
+ sort -n
+ tail .ducks
123 etc
424 bin
796 total



$ sh -xv ~/bin/ducks  
#!/usr/bin/env bash

# Find the disk hog
# Borrowed from http://oreilly.com/pub/h/15
...
...

2
Un conseil connexe: j'ai pris l'habitude de mettre emulate sh 2>/dev/nullau sommet de mes scripts shell. Lorsqu'il est exécuté avec zsh, cela le met en mode compatible POSIX. Lorsqu'elle est exécutée avec d'autres shells, la ligne n'a aucun effet. Ensuite, je peux exécuter le script avec zsh -x /path/to/script. J'aime zsh ici car il fournit de meilleures traces que bash ou ksh.
Gilles, arrête de faire le mal '

7

Shawn J. Goff a soulevé beaucoup de points positifs, mais n'inclut pas toute l'histoire:

Ubuntu n’utilise pas le shell bash, mais un très similaire appelé dash. Les scripts nécessitant bash peuvent ne pas fonctionner correctement lorsqu'ils sont appelés à l'aide d'un /bin/shscript, car vous venez d'appeler un script bash à l'aide de l'interpréteur de tirets.

Beaucoup de scripts système (comme dans init.d, dans / etc et ainsi de suite) ont un shebang #!/bin/sh, mais /bin/shsont en fait un lien symbolique vers un autre shell - autrefois /bin/bash, de nos jours /bin/dash. Mais lorsque l’un d’eux est appelé /bin/sh, ils se comportent différemment, c’est-à-dire qu’ils restent en mode de compatibilité POSIX.

comment font-ils ça? Eh bien, ils inspectent comment ils ont été invoqués.

Un script de shell peut-il tester lui-même comment il a été appelé et faire différentes choses, en fonction de cela? Oui il peut. Ainsi, la façon dont vous l'invoquez peut toujours conduire à des résultats différents, mais bien entendu, il est rarement fait pour vous embêter. :)

En règle générale: si vous apprenez un shell spécifique comme bash et écrivez des commandes à partir d'un tutoriel, insérez #!/bin/bashle titre, pas #!/bin/sh, sauf indication contraire. Sinon, vos commandes pourraient échouer. Et si vous n'avez pas écrit vous-même un script, appelez-le directement ( ./foo.sh, bar/foo.sh) au lieu de deviner un shell ( sh foo.sh, sh bar/foo.sh). Le shebang devrait invoquer la bonne coque.

Et voici deux autres types d'invocation:

cat foo.sh | dash
dash < foo.sh

5

.et sourcesont équivalentes en ce sens qu’elles ne génèrent pas de sous-processus mais exécutent des commandes dans le shell actuel. Ceci est important lorsque le script définit des variables d'environnement ou modifie le répertoire de travail actuel.

Utiliser le chemin ou le donner à /bin/shcrée un nouveau processus dans lequel les commandes sont exécutées.


2
sh script
bash script

Je me demande s'il y a plus ...

.et sourcesont les mêmes. Après exécution, tous les changements d’environnement scriptseraient conservés. Habituellement, il est utilisé pour créer une bibliothèque Bash, afin que celle-ci puisse être réutilisée dans de nombreux scripts différents.

C'est aussi un bon moyen de garder le répertoire actuel. Si vous changez de répertoire dans un script, il ne sera pas appliqué dans le shell dans lequel vous exécutez ce script. Mais si vous le sourcez pour l'exécuter, une fois le script terminé, le répertoire actuel sera conservé.


2
.ne fonctionne que dans sh / bash et les coquilles connexes. sourcefonctionne également en csh et les coquilles connexes.
KeithB


1
. ./filename
# ( dot space dot slash filename )

Exécute le script dans le shell actuel lorsque le répertoire ne se trouve pas dans le chemin.


1

. et la source sont un peu différentes en zsh au moins (c'est ce que j'utilise) car

source file

Fonctionne, tandis que

. file

ne pas, il faut

. ./file
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.