Existe-t-il un moyen pour le script shell de savoir quel programme l'a exécuté?

13

Dans le monde * nix, existe-t-il un moyen pour le script shell d'avoir des informations sur le programme qui l'a exécuté?


Exemple:

/path/to/script1 /path/to/script_xyz

dans ce scénario imaginaire, script_xyzaurait des informations de chemin ( /path/to/script1)

ou

processus PID

de l'entité qui l'a exécuté.

Remarque: je suis curieux de connaître différentes solutions et approches, je ne m'attends pas à ce que cela soit réellement possible

Miloš Đakonović
la source
3
Votre sujet demande quel programme a exécuté le script. Mais votre vraie question semble demander l' interprète du script. Laquelle des deux est vraiment votre question?
kasperd
@kasperd Vous avez raison. La question portait sur le programme, mais c'est en fait l'interprète. C'est pourquoi j'avais le sentiment que ce n'était pas possible en premier lieu.
Miloš Đakonović

Réponses:

23

Il y a souvent une confusion entre le forking de processus et l'exécution.

Lorsque vous le faites à l'invite d'un bashshell.

$ sh -c 'exec env ps'

Le processus P1 émettant cette $invite exécute actuellement du bashcode. Ce bashcode crée un nouveau processus P2 qui s'exécute /bin/shpuis s'exécute /usr/bin/env, qui s'exécute ensuite /bin/ps.

Alors P2 a à son tour exécuté code bash, sh, envet ps.

ps(ou toute autre commande comme un script que nous utiliserions à la place ici) n'a aucun moyen de savoir qu'il a été exécuté par la envcommande.

Tout ce qu'il peut faire est de découvrir quel est son identifiant de processus parent, qui dans ce cas serait P1 ou 1si P1 est mort dans l'intervalle ou sous Linux un autre processus qui a été désigné comme sous- segment à la place de 1.

Il peut alors interroger le système pour savoir quelle commande ce processus est en cours d' exécution (comme avec readlink /proc/<pid>/exeLinux) ou quels arguments ont été passés à la dernière commande qu'il a exécutée (comme avec ps -o args= -p <pid>).

Si vous voulez que votre script sache ce qui l'a invoqué, un moyen fiable serait de le faire dire à l'invocateur. Cela pourrait être fait par exemple via une variable d'environnement. Par exemple, script1pourrait s'écrire:

#! /bin/sh -
INVOKER=$0 script2 &

Et script2:

#! /bin/sh -
printf '%s\n' "I was invoked by $INVOKER"
# and in this case, we'll probably find the parent process is 1
# (if not now, at least one second later) as script1 exited just after
# invoking script2:
ps -fp "$$"
sleep 1
ps -fp "$$"
exit

$INVOKERcontiendra ( généralement ) un chemin vers script1. Dans certains cas, il peut cependant s'agir d'un chemin relatif et le chemin sera relatif au répertoire de travail en cours au moment du script1démarrage. Donc, si vous script1changez le répertoire de travail actuel avant d'appeler script2, script2vous obtiendrez des informations erronées sur ce qui l'a appelé. Il peut donc être préférable de s'assurer qu'il $INVOKERcontient un chemin absolu (de préférence en gardant le nom de base) comme en écrivant script1:

#! /bin/sh -
mypath=$(
  mydir=$(dirname -- "$0") &&
  cd -P -- "$mydir" &&
  pwd -P) && mypath=$mypath/$(basename -- "$0") || mypath=$0

... some code possibly changing the current working directory
INVOKER=$mypath script2

Dans les shells POSIX, $PPIDcontiendra le pid du parent du processus qui a exécuté le shell au moment de son initialisation. Après cela, comme vu ci-dessus, le processus parent peut changer si le processus d'ID $PPIDmeurt.

zshdans le zsh/systemmodule, peut interroger le courant pid parent de l'enveloppe de courant (sous-) avec $sysparams[ppid]. Dans les shells POSIX, vous pouvez obtenir le ppid actuel du processus qui a exécuté l'interpréteur (en supposant qu'il est toujours en cours d'exécution) avec ps -o ppid= -p "$$". Avec bash, vous pouvez obtenir le ppid du (sous-) shell actuel avec ps -o ppid= -p "$BASHPID".

Stéphane Chazelas
la source
8

Oui, un programme peut savoir qui est son parent.

Pour illustrer cela, créons deux scripts bash. Le premier rapporte son PID et démarre le second script:

$ cat s1.sh
#!/bin/bash
echo s1=$$
bash s2.sh

Le deuxième script rapporte son ID de processus, le PID de son parent et la ligne de commande utilisée pour exécuter le parent:

$ cat s2.sh
#!/bin/bash
echo s2=$$ PPID=$PPID
echo "Parent command: $(ps -o cmd= -q $PPID)"

Maintenant, exécutons-le:

$ bash s1.sh
s1=17955
s2=17956 PPID=17955
Parent command: bash s1.sh

Comme vous pouvez le voir, le deuxième script connaît en fait le PID de son parent. En utilisant ps, ce PID révèle la ligne de commande utilisée pour appeler le parent.

Pour une discussion plus approfondie du PPID, voir la réponse de Stéphane Chazelas .

John1024
la source
Merci. Exécuter vos exemples de scripts que j'obtiens s1, s2et les PPIDvaleurs mais ensuite, sur plusieurs lignes après ERROR: Unsupported SysV option.et plusieurs lignes avec des explications supplémentaires et - valeur vide pourParent command
Miloš Đakonović
John utilise une fonctionnalité ps qui n'est pas disponible (ou qui est fournie différemment) sur votre plate-forme, consultez votre page de manuel ps (1).
Jasen
@Miloshio J'ai testé ce qui précède à l'aide psdu procps-ngpackage, version 3.3.12. Comme Jasen l'a suggéré, vous utilisez probablement une version différente qui peut nécessiter une syntaxe différente pour imprimer la ligne de commande du parent. Essayez ps -f | grep $PPID.
John1024