Jetez un œil au 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, deux utilisateurs sont 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 en y de différentes manières, comme mentionné 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
Remarque: j'ai passé 1 et uid 1000 à tous ces exécutables.
Pouvez-vous maintenant expliquer les différences entre tous ces éléments?
command-line
bash
scripts
Ramana Reddy
la source
la source
Réponses:
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écutezcommand
, 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 courantfile.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 lesh
shell. Sur les systèmes Ubuntu, c'est le casdash
et nonbash
. 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: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:
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: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 defoo
(car elle est également définie dans le script) mais la valeur defoo
dans le shell parent sera inchangée: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: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 deps
. 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:Et exécutez-le de différentes manières avec un seul terminal ouvert:
Lancement direct,
./foo.sh
.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é commebash foo.sh
, il ne sera affiché quefoo.sh
ce qui signifie que vousgrep
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 laps
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 ilps
affiche sa sortie.Lancement direct avec shell explicite (bash)
Ici, parce que vous utilisez avec
bash foo.sh
, la sortie deps
sera affichéebash 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 deps
) tous affichés parce que maintenantps
affichera chacun d'eux parce que votre commande inclura le motbash
.Lancement direct avec un autre shell (
sh
)C'est différent parce que vous exécutez le script avec
sh
et nonbash
. Par conséquent, la seulebash
instance est le shell parent où vous avez lancé votre script. Tous les autres obus mentionnés ci-dessus sont gérés par à lash
place.Sourcing (soit par
.
ousource
, même chose)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'utiliserpgrep
. Tous ces problèmes auraient été évités si vous veniez de courirAinsi, une version de travail de votre script qui imprime toujours le bon numéro est (notez l'absence de substitution de commande):
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 pasbash
.la source
./foo.sh
s'exécute dans un nouveau shell qui n'est pas une copie du parent. Par exemple, si vous définissezfoo="bar"
dans votre terminal puis exécutez un script qui s'exécuteecho $foo
, vous obtiendrez une ligne vide car le shell du script n'aura pas hérité de la valeur de la variable.pgrep
est un binaire distinct, et oui, il est exécuté par le script que vous exécutez.