Regardez le code:
#!/bin/bash
read -p "Eneter 1 for UID and 2 for LOGNAME" choice
if [ $choice -eq 1 ]
then
read -p "Enter UID: " uid
logname=`cat /etc/passwd | grep $uid | cut -f1 -d:`
else
read -p "Enter Logname: " logname
fi
not=`ps -au$logname | grep -c bash`
echo "The number of terminals opened by $logname are $not"
Ce code permet de connaître le nombre de terminaux ouverts par un utilisateur sur le même PC. Maintenant, il y a deux utilisateurs connectés, disons x et y. Je suis actuellement connecté en tant que y et il y a 3 terminaux ouverts dans l'utilisateur x. Si j'exécute ce code de y de différentes manières, comme indiqué ci-dessus, les résultats sont les suivants:
$ ./file.sh
The number of terminals opened by x are 3
$ bash file.sh
The number of terminals opened by x are 5
$ sh file.sh
The number of terminals opened by x are 3
$ source file.sh
The number of terminals opened by x are 4
$ . ./file.sh
The number of terminals opened by x are 4
Note: J'ai passé 1 et uid 1000 à tous ces exécutables.
Pouvez-vous maintenant expliquer les différences entre tous ces éléments?
La seule différence majeure est entre la recherche et l’exécution d’un script. source foo.sh
le recherchera et tous les autres exemples que vous montrez sont en cours d'exécution. Plus en détail:
./file.sh
Ceci exécutera un script appelé file.sh
qui se trouve dans le répertoire en cours (./
). Normalement, lorsque vous exécutez command
, le shell recherchera dans les répertoires de votre $PATH
un fichier exécutable appelé command
. Si vous indiquez un chemin complet, tel que /usr/bin/command
ou ./command
, 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 file.sh
dans le répertoire actuel, il cherche dans le répertoire parent (../
).
sh file.sh
Cet équivalent de sh ./file.sh
, comme ci-dessus, exécutera le script appelé file.sh
dans le répertoire en cours. La différence est que vous l'exécutez explicitement avec le shell sh
. Sur les systèmes Ubuntu, il s'agit de dash
et non bash
. Généralement, les scripts ont une ligne Shebang qui donne le programme sous lequel ils doivent être exécutés. Les appeler avec un autre remplace cela. 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 imprimera 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
Ainsi, appeler un script avec Shell script
écrasera la ligne Shebang (le cas échéant) et exécutera le script avec le shell que vous lui direz.
source file.sh
ou . file.sh
Ceci est appelé, assez étonnamment, la recherche du script . Le mot clé source
est un alias de la commande intégrée .
du shell. C'est une façon d'exécuter le script dans le shell actuel. Normalement, lorsqu'un script est exécuté, il est exécuté dans son propre shell, qui est différent de celui en cours. Pour illustrer:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Maintenant, si je mets la variable foo
sur quelque chose d'autre dans le Shell parent puis exécute le script, le script imprimera une valeur différente de foo
(car elle est également définie dans le script), mais la valeur de foo
dans le Shell parent être inchangé:
$ 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 afin que la valeur de foo
dans le parent soit 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, la sourcing est utilisée 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 les variables Shell et les rendre disponibles à la fin du script.
Compte tenu de tout cela, 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. Il compte le nombre de fois où 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 cela, mais c'est une autre discussion). Pour clarifier, j'ai simplifié un peu votre script à ceci:
#!/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 manière dont le script est affiché dans la sortie de ps
. Au lieu d'être répertorié en tant que bash foo.sh
, il ne sera affiché que par foo.sh
, ce qui signifie que votre grep
l'aura oublié. Trois instances de bash sont en cours d'exécution: le processus parent, le bash exécutant le script et un autre exécutant la commande ps
. Ce dernier point est important, le lancement d’une commande avec substitution de commande (`command`
ou $(command)
) entraîne l’envoi d’une copie du shell parent et l’exécution de la commande. Ici, cependant, aucun d’entre eux n’est affiché à cause de la façon dont ps
affiche sa sortie.
Lancement direct avec Shell explicite (bash)
$ bash foo.sh
The number of shells opened by terdon is 3
Ici, étant donné que vous utilisez bash foo.sh
, la sortie de ps
affichera bash foo.sh
et sera comptée. Nous avons donc ici le processus parent, le bash
exécutant le script et le shell cloné (exécutant le ps
), car maintenant, ps
montrera chacun d’entre eux car 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
Ceci est différent parce que vous exécutez le script avec sh
et non bash
. Par conséquent, la seule instance bash
est le shell parent où vous avez lancé votre script. Tous les autres obus mentionnés ci-dessus sont gérés par sh
à la place.
Sourcing (par .
ou source
, même chose)
$ . ./foo.sh
The number of shells opened by terdon is 2
Comme je l'ai expliqué ci-dessus, la création d'un script entraîne son exécution dans le même shell que le processus parent. Cependant, un sous-shell distinct est lancé pour lancer la commande ps
, ce qui porte le total à deux.
Pour terminer, la méthode correcte pour 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 aviez juste lancé
pgrep -cu terdon bash
Ainsi, une version de travail de votre script qui affiche toujours le bon nombre est (notez l'absence de substitution de commande):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Cela renverra 1 à la source et 2 (car un nouveau bash sera lancé pour exécuter le script) pour tous les autres modes de lancement. Il retournera toujours 1 lorsqu’il sera lancé avec sh
puisque le processus enfant n’est pas bash
.