Gibt es eine Möglichkeit für Shell-Skripte zu wissen, welches Programm es ausgeführt hat?

13

Gibt es in * nix world eine Möglichkeit für Shell-Skripte, Informationen darüber zu erhalten, welches Programm es ausgeführt hat?


Beispiel:

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

in diesem imaginären Szenario script_xyzhätte Pfadinformationen ( /path/to/script1)

oder

PID verarbeiten

der Einheit, die es ausgeführt haben.

Hinweis: Ich bin neugierig auf verschiedene Lösungen und Ansätze und erwarte nicht, dass genau dies tatsächlich möglich ist

Miloš Đakonović
quelle
3
Ihr Motiv fragt , welches Programm ausgeführt das Skript. Ihre eigentliche Frage scheint jedoch nach dem Interpreter des Skripts zu fragen . Um welche der beiden geht es in Ihrer Frage wirklich?
Kasperd
@kasperd Du hast recht. Frage war über Programm, aber das ist eigentlich Dolmetscher. Deshalb hatte ich das Gefühl, dass dies überhaupt nicht möglich ist.
Miloš Đakonović

Antworten:

23

Es gibt oft eine Verwechslung zwischen Prozess-Forking und Ausführung.

Wenn Sie an der Eingabeaufforderung einer bashShell tun .

$ sh -c 'exec env ps'

Der Prozess P1 , der diese $Eingabeaufforderung ausgibt, führt derzeit bashCode aus. Dieser bashCode gibt einen neuen Prozess P2 aus , der ausgeführt wird, /bin/shder dann ausgeführt wird /usr/bin/envund der dann ausgeführt wird/bin/ps .

So P2 wiederum hat Code ausgeführt bash, sh, envund ps.

ps(oder ein anderer Befehl wie ein Skript, den wir stattdessen hier verwenden würden) kann nicht wissen, dass er vom envBefehl ausgeführt wurde .

Alles, was es tun kann, ist herauszufinden, wie die übergeordnete Prozess-ID lautet. In diesem Fall ist dies entweder P1, oder 1wenn P1 im Intervall abgestorben ist, oder unter Linux ein anderer Prozess, der stattdessen als Subreaper festgelegt wurde1 .

Anschließend kann das System abgefragt werden, welcher Befehl von diesem Prozess gerade ausgeführt wird (wie bei readlink /proc/<pid>/exeLinux) oder welche Argumente an den zuletzt ausgeführten Befehl übergeben wurden (wie bei Linux)ps -o args= -p <pid> ).

Wenn Sie möchten, dass Ihr Skript weiß, was es aufgerufen hat, können Sie es auf zuverlässige Weise vom Aufrufer mitteilen lassen. Dies kann beispielsweise über eine Umgebungsvariable erfolgen. Zum Beispiel script1könnte geschrieben werden als:

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

Und 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

$INVOKERwird ( allgemein ) einen Pfad zu enthalten script1. In einigen Fällen kann es sich jedoch um einen relativen Pfad handeln, der sich auf das aktuelle Arbeitsverzeichnis zum Zeitpunkt des script1Starts bezieht. Wenn Sie also script1das aktuelle Arbeitsverzeichnis vor dem Aufruf ändern script2, script2erhalten Sie falsche Informationen darüber, wie es aufgerufen wurde. Es kann daher vorzuziehen sein, sicherzustellen, dass $INVOKERein absoluter Pfad enthalten ist (vorzugsweise unter Beibehaltung des Basisnamens), indem Sie wie folgt schreiben 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

Enthält in POSIX-Shells $PPIDdie PID des übergeordneten Prozesses, der die Shell zum Zeitpunkt der Shell-Initialisierung ausgeführt hat. Danach kann sich, wie oben gezeigt, der übergeordnete Prozess ändern, wenn der Prozess von id $PPIDstirbt.

zshIm zsh/systemModul kann mit die aktuelle Eltern-PID der aktuellen (Sub-) Shell abgefragt werden $sysparams[ppid]. In POSIX-Shells können Sie die aktuelle ppid des Prozesses abrufen, mit dem der Interpreter ausgeführt wurde (sofern er noch ausgeführt wird) ps -o ppid= -p "$$". Mit bashkönnen Sie die ppid der aktuellen (Sub-) Shell mit erhalten ps -o ppid= -p "$BASHPID".

Stéphane Chazelas
quelle
8

Ja, ein Programm kann wissen, wer sein Elternteil ist.

Zur Veranschaulichung erstellen wir zwei Bash-Skripte. Der erste meldet seine PID und startet das zweite Skript:

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

Das zweite Skript gibt die Prozess-ID, die PID des übergeordneten Skripts und die zum Ausführen des übergeordneten Skripts verwendete Befehlszeile an:

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

Jetzt lass es uns laufen:

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

Wie Sie sehen, kennt das zweite Skript die PID seines übergeordneten Skripts. Unter Verwendung psdieser PID wird die Befehlszeile angezeigt, die zum Aufrufen des übergeordneten Elements verwendet wird.

Weitere Informationen zu PPID finden Sie in der Antwort von Stéphane Chazelas .

John1024
quelle
Vielen Dank. Laufen Ihre Beispielskripte ich s1, s2und PPIDWerte , aber dann, in mehreren Linien nach ERROR: Unsupported SysV option.und mehrere Zeilen mit zusätzlichen Erläuterungen und - leeren Wert fürParent command
Miloš Đakonović
Wenn Sie eine ps-Funktion verwenden, die auf Ihrer Plattform nicht verfügbar ist (oder anders bereitgestellt wird), lesen Sie die ps (1) -Handbuchseite.
Jasen
@Miloshio Ich habe das obige mit psdem procps-ngPaket, Version 3.3.12, getestet. Wie Jasen vorgeschlagen hat, verwenden Sie wahrscheinlich eine andere Version, für die möglicherweise eine andere Syntax zum Drucken der Befehlszeile des übergeordneten Elements erforderlich ist. Versuchen Sie es ps -f | grep $PPID.
John1024