web-dev-qa-db-fra.com

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

Il existe plusieurs façons d'exécuter un script, celles que je connais sont:

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

En sont-ils plus? Quelles sont les différences entre eux? Y a-t-il des situations où je dois utiliser l'une et non une autre?

44
phunehehe

Une autre façon consiste à appeler l'interpréteur et à lui passer 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 à la fois dans les shells liés à bash et csh.) Il exécute le script dans -place (comme si vous aviez copié et collé le script juste là). Cela signifie que toutes les fonctions et variables non locales du script restent. Cela signifie également que si le script fait un cd dans un répertoire, vous serez toujours là quand c'est fait.

Les autres façons d'exécuter un script l'exécuteront dans son propre sous-shell. Les variables du script ne sont pas encore actives une fois terminé. Si le script a changé de répertoire, cela n'affecte pas l'environnement appelant.

/ path/to/script et/bin/sh sont légèrement différents. En règle générale, un script a un "Shebang" au début qui ressemble à ceci:

#! /bin/bash

Il s'agit du chemin d'accès à l'interpréteur de script. S'il spécifie un interpréteur 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 Ruby commencent par (respectivement):

#! /bin/Perl

et

#! /bin/Ruby

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

Ubuntu n'utilise en fait 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 en faisant /bin/sh script parce que vous venez d'appeler un script bash à l'aide de l'interpréteur de tirets.

Une autre petite différence entre appeler directement le script et passer le chemin du script à l'interpréteur est que le script doit être marqué comme exécutable pour l'exécuter directement, mais pas pour l'exécuter en passant le chemin à l'interpréteur.

Une autre variante mineure: vous pouvez préfixer l'une de ces façons 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 que je l'inclurais pour plus de précision.

32
Shawn J. Goff

La plupart des gens déboguent les scripts Shell en ajoutant ce qui suit indicateurs de débogage 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 avez les autorisations pour 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 le 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
...
...
9
Stefan Lasiewski

Shawn J. Goff a fait beaucoup de bons points, mais n'a pas inclus toute l'histoire:

Ubuntu n'utilise en fait 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 en faisant /bin/sh script 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/sh est en fait un lien symbolique vers un autre Shell - autrefois /bin/bash, aujourd'hui /bin/dash. Mais lorsque l'un d'eux est invoqué comme /bin/sh, ils se comportent différemment, c'est-à-dire qu'ils s'en tiennent au mode de compatibilité POSIX.

Comment font-ils ça? Eh bien, ils inspectent la façon dont ils ont été invoqués.

Un shellscript peut-il tester lui-même comment il a été invoqué et faire différentes choses, selon cela? Oui il peut. Ainsi, la façon dont vous l'invoquez peut toujours conduire à des résultats différents, mais bien sûr, cela est rarement fait pour vous ennuyer. :)

En règle générale: si vous apprenez un shell spécifique comme bash et écrivez des commandes à partir d'un didacticiel bash, mettez #!/bin/bash dans le titre, pas #!/bin/sh, sauf indication contraire. Sinon, vos commandes pourraient échouer. Et si vous n'avez pas écrit de script vous-même, invoquez-le directement (./foo.sh, bar/foo.sh) au lieu de deviner un Shell (sh foo.sh, sh bar/foo.sh). Le Shebang devrait invoquer le bon Shell.

Et voici deux autres types d'invocation:

cat foo.sh | dash
dash < foo.sh
7
user unknown

. et source sont équivalents en ce sens qu'ils 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/sh crée un nouveau processus dans lequel les commandes sont exécutées.

5
mouviciel
sh script
bash script

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

. et source sont identiques. Après l'exécution, tout changement d'environnement dans script serait conservé. Habituellement, il serait utilisé pour générer une bibliothèque Bash, de sorte que la bibliothèque peut être réutilisée dans de nombreux scripts différents.

C'est aussi un bon moyen de conserver le répertoire actuel. Si vous changez de répertoire dans le script, il ne sera pas appliqué dans le shell que vous exécutez ce script. Mais si vous le sourcez pour l'exécuter, après la fermeture du script, le répertoire courant sera conservé.

2
livibetter

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

source file

Fonctionne, tandis que

. file

non, il faut

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

Exécute le script dans le shell actuel lorsque le répertoire n'est pas dans le chemin.

1
jrh_enginnering

" serland exec " compte-t-il d'une manière différente? Exec Userland charge le code et le fait exécuter sans l'utilisation d'un appel système execve ().

1
Bruce Ediger