La seule différence majeure est entre l'approvisionnement et l'exécution d'un script. source foo.sh
le source et tous les autres exemples que vous montrez sont en cours d'exécution. Plus en détail:
./file.sh
Cela exécutera un script appelé file.sh
qui se trouve dans le répertoire courant ( ./
). Normalement, lorsque vous exécutez command
, le shell recherchera dans les répertoires de votre $PATH
fichier exécutable appelé command
. Si vous donnez un chemin complet, tel que /usr/bin/command
ou ./command
, alors le $PATH
est ignoré et ce fichier spécifique est exécuté.
../file.sh
C'est fondamentalement la même chose que ./file.sh
sauf qu'au lieu de chercher dans le répertoire courant file.sh
, il cherche dans le répertoire parent ( ../
).
sh file.sh
Cet équivalent à sh ./file.sh
, comme ci-dessus, exécutera le script appelé file.sh
dans le répertoire courant. La différence est que vous l'exécutez explicitement avec le sh
shell. Sur les systèmes Ubuntu, c'est le cas dash
et non bash
. Habituellement, les scripts ont une ligne shebang qui donne le programme sous lequel ils doivent être exécutés. Les appeler avec un autre l'emporte. Par exemple:
$ cat foo.sh
#!/bin/bash
## The above is the shebang line, it points to bash
ps h -p $$ -o args='' | cut -f1 -d' ' ## This will print the name of the shell
Ce script affichera simplement le nom du shell utilisé pour l'exécuter. Voyons ce qu'il retourne lorsqu'il est appelé de différentes manières:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
Donc, appeler appeler un script avec shell script
remplacera la ligne shebang (si elle est présente) et exécutera le script avec le shell que vous lui direz.
source file.sh
ou . file.sh
Cela s'appelle, de façon assez surprenante, l' approvisionnement du script. Le mot source
- clé est un alias de la .
commande intégrée du shell . C'est une façon d'exécuter le script dans le shell courant. Normalement, lorsqu'un script est exécuté, il est exécuté dans son propre shell, différent de celui en cours. Pour illustrer:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Maintenant, si je définis la variable foo
sur quelque chose d'autre dans le shell parent et que j'exécute ensuite le script, le script affichera une valeur différente de foo
(car elle est également définie dans le script) mais la valeur de foo
dans le shell parent sera inchangée:
$ foo="Parent"
$ bash foo.sh
Foo (script) is Script ## This is the value from the script's shell
$ echo "$foo"
Parent ## The value in the parent shell is unchanged
Cependant, si je source le script au lieu de l'exécuter, il sera exécuté dans le même shell donc la valeur de foo
dans le parent sera modifiée:
$ source ./foo.sh
Foo (script) is Script ## The script's foo
$ echo "$foo"
Script ## Because the script was sourced,
## the value in the parent shell has changed
Ainsi, le sourcing est utilisé dans les rares cas où vous souhaitez qu'un script affecte le shell à partir duquel vous l'exécutez. Il est généralement utilisé pour définir des variables shell et les rendre disponibles une fois le script terminé.
Avec tout cela à l'esprit, la raison pour laquelle vous obtenez des réponses différentes est, tout d'abord, que votre script ne fait pas ce que vous pensez qu'il fait. Il compte le nombre de fois qui bash
apparaît dans la sortie de ps
. Ce n'est pas le nombre de terminaux ouverts , c'est le nombre de shells en cours d' exécution (en fait, ce n'est même pas ça, mais c'est une autre discussion). Pour clarifier, j'ai un peu simplifié votre script:
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
Et exécutez-le de différentes manières avec un seul terminal ouvert:
Lancement direct, ./foo.sh
.
$ ./foo.sh
The number of shells opened by terdon is 1
Ici, vous utilisez la ligne shebang. Cela signifie que le script est exécuté directement par tout ce qui y est défini. Cela affecte la façon dont le script est affiché dans la sortie de ps
. Au lieu d'être répertorié comme bash foo.sh
, il ne sera affiché que foo.sh
ce qui signifie que vous grep
le manquerez. Il y a en fait 3 instances bash en cours d'exécution: le processus parent, le bash exécutant le script et un autre qui exécute la ps
commande . Ce dernier est important, le lancement d'une commande avec substitution de commande ( `command`
ou $(command)
) entraîne le lancement d'une copie du shell parent et l'exécution de la commande. Ici, cependant, aucun de ces éléments n'est affiché en raison de la façon dont il ps
affiche sa sortie.
Lancement direct avec shell explicite (bash)
$ bash foo.sh
The number of shells opened by terdon is 3
Ici, parce que vous utilisez avec bash foo.sh
, la sortie de ps
sera affichée bash foo.sh
et sera comptée. Donc, ici, nous avons le processus parent, l' bash
exécution du script et le shell cloné (exécution de ps
) tous affichés parce que maintenant ps
affichera chacun d'eux parce que votre commande inclura le mot bash
.
Lancement direct avec un autre shell ( sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
C'est différent parce que vous exécutez le script avec sh
et non bash
. Par conséquent, la seule bash
instance est le shell parent où vous avez lancé votre script. Tous les autres obus mentionnés ci-dessus sont gérés par à la sh
place.
Sourcing (soit par .
ou source
, même chose)
$ . ./foo.sh
The number of shells opened by terdon is 2
Comme je l'ai expliqué ci-dessus, l'approvisionnement d'un script le fait s'exécuter dans le même shell que le processus parent. Cependant, un sous-shell séparé est démarré pour lancer la ps
commande et cela porte le total à deux.
Enfin, la bonne façon de compter les processus en cours n'est pas d'analyser ps
mais d'utiliser pgrep
. Tous ces problèmes auraient été évités si vous veniez de courir
pgrep -cu terdon bash
Ainsi, une version de travail de votre script qui imprime toujours le bon numéro est (notez l'absence de substitution de commande):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Cela renverra 1 lors de la source et 2 (car un nouveau bash sera lancé pour exécuter le script) pour toutes les autres façons de lancer. Il renverra toujours 1 lors du lancement avec sh
puisque le processus enfant ne l'est pas bash
.