Lorsque j'exécute ce script, destiné à s'exécuter jusqu'à ce qu'il soit tué ...
# foo.sh
while true; do sleep 1; done
... je ne le trouve pas en utilisant ps ax
:
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
... mais si j'ajoute juste l'en- #!
tête " " commun au script ...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
... alors le script devient détectable par la même ps
commande ...
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
Pourquoi cela est-il ainsi?
Cela peut être une question connexe: je pensais que " #
" n'était qu'un préfixe de commentaire, et si c'est le cas " #! /usr/bin/bash
" n'est lui-même rien de plus qu'un commentaire. Mais " #!
" a-t-il une signification supérieure à celle d'un simple commentaire?
shell-script
shell
ps
shebang
StoneThrow
la source
la source
Réponses:
Lorsque le shell interactif actuel est
bash
, et que vous exécutez un script sans#!
ligne, puisbash
exécutera le script. Le processus apparaîtraps ax
comme juste dans la sortiebash
.Dans un autre terminal:
En relation:
Les sections pertinentes forment le
bash
manuel:Cela signifie que l'exécution
./foo.sh
sur la ligne de commande, lorsqu'ellefoo.sh
n'a pas de#!
ligne, équivaut à l'exécution des commandes du fichier dans un sous-shell, c'est-à-dire queAvec une
#!
ligne appropriée pointant par exemple/bin/bash
, c'est comme fairela source
ps
le script s'exécute en tant que "/usr/bin/bash ./foo.sh
". Donc, dans le premier cas, comme vous le dites, bash exécutera le script, mais ce script n'aurait-il pas besoin d'être «passé» à l'exécutable bash fourchu, comme dans le second cas? (et si oui, j'imagine que ce serait trouvable avec la pipe à grep ...?)$$
pointe toujours vers l'ancien dans le cas du sous-shell (echo $BASHPID
/bash -c 'echo $PPID'
).Lorsqu'un script shell commence par
#!
, cette première ligne est un commentaire en ce qui concerne le shell. Cependant, les deux premiers caractères sont significatifs pour une autre partie du système: le noyau. Les deux personnages#!
s'appellent un shebang . Pour comprendre le rôle du shebang, vous devez comprendre comment un programme est exécuté.L'exécution d'un programme à partir d'un fichier nécessite une action du noyau. Cela se fait dans le cadre de l'
execve
appel système. Le noyau doit vérifier les autorisations de fichier, libérer les ressources (mémoire, etc.) associées au fichier exécutable en cours d'exécution dans le processus d'appel, allouer des ressources pour le nouveau fichier exécutable et transférer le contrôle au nouveau programme (et plus de choses qui Je ne mentionnerai pas). L'execve
appel système remplace le code du processus en cours d'exécution; il existe un appel système distinctfork
pour créer un nouveau processus.Pour ce faire, le noyau doit prendre en charge le format du fichier exécutable. Ce fichier doit contenir du code machine, organisé d'une manière que le noyau comprend. Un script shell ne contient pas de code machine, il ne peut donc pas être exécuté de cette façon.
Le mécanisme shebang permet au noyau de reporter la tâche d'interprétation du code à un autre programme. Lorsque le noyau voit que le fichier exécutable commence par
#!
, il lit les quelques caractères suivants et interprète la première ligne du fichier (moins l'#!
espace de tête et facultatif) comme un chemin vers un autre fichier (plus les arguments, dont je ne parlerai pas ici ). Lorsque le noyau est invité à exécuter le fichier/my/script
et qu'il voit que le fichier commence par la ligne#!/some/interpreter
, le noyau s'exécute/some/interpreter
avec l'argument/my/script
. C'est ensuite/some/interpreter
à décider qu'il/my/script
s'agit d'un fichier de script qu'il doit exécuter.Que se passe-t-il si un fichier ne contient pas de code natif dans un format que le noyau comprend et ne commence pas par un shebang? Eh bien, le fichier n'est pas exécutable et l'
execve
appel système échoue avec le code d'erreurENOEXEC
(erreur de format exécutable).Cela pourrait être la fin de l'histoire, mais la plupart des shells implémentent une fonction de secours. Si le noyau revient
ENOEXEC
, le shell regarde le contenu du fichier et vérifie s'il ressemble à un script shell. Si le shell pense que le fichier ressemble à un script shell, il l'exécute lui-même. Les détails de la façon dont cela fonctionne dépendent du shell. Vous pouvez voir ce qui se passe en ajoutantps $$
dans votre script, et plus en observant le processus avecstrace -p1234 -f -eprocess
où 1234 est le PID du shell.En bash, ce mécanisme de secours est implémenté en appelant
fork
mais pasexecve
. Le processus bash enfant efface lui-même son état interne et ouvre le nouveau fichier de script pour l'exécuter. Par conséquent, le processus qui exécute le script utilise toujours l'image de code bash d'origine et les arguments de ligne de commande d'origine passés lorsque vous avez appelé bash à l'origine. ATT ksh se comporte de la même manière.Dash, en revanche, réagit
ENOEXEC
en appelant/bin/sh
avec le chemin d'accès au script passé en argument. En d'autres termes, lorsque vous exécutez un script sans shebang à partir du tableau de bord, cela se comporte comme si le script avait une ligne de shebang avec#!/bin/sh
. Mksh et zsh se comportent de la même manière.la source
bash
est forké, il a accès au mêmeargv[]
tableau que son parent, c'est ainsi qu'il connaît "les arguments de ligne de commande d'origine passés lorsque vous avez invoqué bash à l'origine", et si donc, c'est pourquoi l'enfant n'est pas passé le script d'origine comme argument explicite (d'où pourquoi il n'est pas trouvé par le grep) - est-ce exact?BINFMT_SCRIPT
module le contrôle et peut être supprimé / modularisé, bien qu'il soit généralement lié statiquement au noyau), mais je ne vois pas pourquoi vous voudriez, sauf peut-être dans un système embarqué . Pour contourner cette possibilité,bash
a un indicateur de configuration (HAVE_HASH_BANG_EXEC
) pour compenser!ps
présente comme arguments de ligne de commande, mais seulement jusqu'à un certain point: il doit modifier un tampon de mémoire existant, il ne peut pas agrandir ce tampon. Donc, si bash essayait de modifier sonargv
pour ajouter le nom du script, cela ne fonctionnerait pas toujours. L'enfant n'est pas «passé d'argument» car il n'y a jamais d'execve
appel système chez l'enfant. C'est juste la même image de processus bash qui continue de fonctionner.Dans le premier cas, le script est exécuté par un enfant dérivé de votre shell actuel.
Vous devez d'abord exécuter
echo $$
, puis jeter un œil à un shell qui a l'ID de processus de votre shell comme ID de processus parent.la source