So erhalten Sie die PID der Subshell in Korn Shell (entspricht $ BASHPID)

8

In bash haben Sie diese praktische Variable: $ BASHPID, die immer die PID der aktuell ausgeführten Subshell zurückgibt. Wie kann ich die PID einer Subshell in ksh erhalten? Siehe zum Beispiel den folgenden Code:

#!/usr/bin/ksh93

echo "PID at start: $$"

function run_in_background {
  echo "PID in run_in_background $$"
  run_something &
  echo "PID of backgrounded run_something: $!"
}

function run_something {
  echo "*** PID in run_something: $$"
  sleep 10;
}    

run_in_background
echo "PID of run in background $!"

Dies gibt Folgendes aus:

PID at start: 5328
PID in run_in_background 5328
*** PID in run_something: 5328
PID of backgrounded run_something: 5329
PID of run in background 5329

Was ich möchte, ist die Zeile, die mit ****der Ausgabe der PID der Subshell beginnt, im Fall des Beispiels wäre dies 5329.

Patkos Csaba
quelle

Antworten:

10

Ich glaube nicht, dass das in ksh verfügbar ist. Es gibt eine POSIX-Lösung, bei der ein externer Prozess ausgeführt wird:

sh -c 'echo $PPID'

Unter Linux readlink /proc/selfwürde das auch funktionieren, aber ich sehe keinen Vorteil (es könnte geringfügig schneller sein; es könnte bei einer BusyBox-Variante nützlich sein, die es hat, readlinkaber nicht $PPID, aber ich glaube nicht, dass es einen gibt).

Beachten Sie, dass Sie darauf achten müssen, diesen Befehl nicht in einer kurzlebigen Sub-Sub-Shell auszuführen, um den Wert in der Shell zu erhalten. Beispielsweise wird p=$(sh -c 'echo $PPID')möglicherweise die Ausgabe der Subshell angezeigt, die shinnerhalb der Befehlssubstitution aufgerufen wird (oder nicht, einige Shells optimieren diesen Fall). Führen Sie stattdessen aus

p=$(exec sh -c 'echo $PPID')
Gilles 'SO - hör auf böse zu sein'
quelle
Ich habe diesen Vorschlag bereits gesehen, aber er funktioniert nicht. Es gibt mir eine dritte PID ... aber ich werde es am Montag erneut testen, wenn ich wieder bei der Arbeit bin.
Patkos Csaba
@PatkosCsaba In ksh würde es wahrscheinlich funktionieren, weil ksh Gabeln optimiert, aber in einigen anderen Shells wie Bash müssen Sie vorsichtig sein, um nicht versehentlich die PID einer Sub-Sub-Shell zu erhalten. Siehe meine aktualisierte Antwort.
Gilles 'SO - hör auf böse zu sein'
Dies funktioniert: $(exec sh -c 'echo $PPID')Der anfängliche einfache Befehl sh -c 'echo $PPID'gibt jedoch eine dritte PID an. Also danke für die Lösung. Akzeptiert.
Patkos Csaba
1
@FranklinYu Das liegt daran, dass keiner eine Subshell erstellt. Bash wird optimiert (sh -c 'echo $PPID'), um das Erstellen einer Subshell zu vermeiden. Kontrast zu (sh -c 'echo $PPID'; true). Diese Optimierung wird nur $BASHPIDaktiviert, wenn Sie versuchen, als letztes vor dem Beenden der Subshell auf sie zuzugreifen, dh nur in Fällen, in denen Sie mit dem Wert nichts anfangen können. So in der Praxis können Sie ersetzen $BASHPIDmit $(sh -c 'echo $PPID').
Gilles 'SO - hör auf böse zu sein'
1
@FranklinYu Ich glaube nicht, dass irgendetwas eingebaut ist. Sie können natürlich die tragbare Methode verwenden sh -c 'echo $PPID'.
Gilles 'SO - hör auf böse zu sein'
3

Sie können erreichen, was Sie wollen, aber Sie müssen run_something in ein separates Skript einfügen. Ich bin mir nicht ganz sicher, warum, aber $$ wird nicht neu bewertet, wenn es in einer Funktion im selben Skript verwendet wird, das es aufruft. Ich vermute, dass der Wert von $$ einmal zugewiesen wird, nachdem das Skript analysiert wurde und bevor es ausgeführt wird.

run_in_background.sh

#
echo "PID at start: $$"

    function run_in_background {
      echo "PID in run_in_background $$"
      ./run_something.sh &
      echo "PID of backgrounded run_something: $!"
    }

    run_in_background
    echo "PID of run in background $!"

run_something.sh

#
echo "*** PID in run_something: $$"
sleep 10;

Ausgabe

PID at start: 24395
PID in run_in_background 24395
PID of backgrounded run_something: 24396
PID of run in background 24396
*** PID in run_something: 24396
Justin Rowe
quelle
1
# KSH_VERSION hasn't always been a nameref, nor has it always existed.
# Replace with a better test if needed. e.g.:
# https://www.mirbsd.org/cvs.cgi/contrib/code/Snippets/getshver?rev=HEAD
if [[ ${!KSH_VERSION} == .sh.version ]]; then
    # if AT&T ksh
    if builtin pids 2>/dev/null; then # >= ksh93 v- alpha
        function BASHPID.get { .sh.value=$(pids -f '%(pid)d'); }
    elif [[ -r /proc/self/stat ]]; then # Linux / some BSDs / maybe others
        function BASHPID.get { read -r .sh.value _ </proc/self/stat; }
    else # Crappy fallback
        function BASHPID.get { .sh.value=$(exec sh -c 'echo $PPID'); }
    fi
elif [[ ! ${BASHPID+_} ]]; then
   echo 'BASHPID requires Bash, ksh93, or mksh >= R41' >&2
   exit 1
fi
Ormaaj
quelle
Zwei Nitpicks: if [[ ${!KSH_VERSION} = .sh.version ]]; then(nur einer =) und elif [[ -z ${BASHPID+_} ]]; then(Vermeiden Sie die Verwendung des Impliziten -nin doppelten eckigen Klammern, das alte pdksh wusste es nicht).
Mirabilos
0

Nach der Antwort von @Gilles, auf die ich bei der Lösung eines anderen Problems gestoßen bin, habe ich ein schnelles Testprogramm zusammengestellt, das die Theorie untermauert, dass die richtige Antwort lautet:

MYPID=$(exec sh -c 'echo $PPID')

Ich habe festgestellt, dass es Zeiten gibt, in denen dies execnicht erforderlich ist, aber ich habe bestätigt, dass es die einzige Möglichkeit ist, in allen von mir ausprobierten Shells immer die richtige PID zu erhalten. Hier ist mein Test:

#!/bin/sh
pids() {
  echo ------
  pstree -pg $PPID
  echo 'PPID = ' $PPID
  echo '$$ = ' $$
  echo 'BASHPID =' $BASHPID
  echo -n 'sh -c echo $PPID = '; sh -c 'echo $PPID'
  echo -n '$(sh -c '\''echo $PPID'\'') = '; echo $(sh -c 'echo $PPID') 
  echo -n '$(exec sh -c '\''echo $PPID'\'') = '; echo $(exec sh -c 'echo $PPID') 
  echo -n 'p=$(sh -c '\''echo $PPID'\'') = '; p=$(sh -c 'echo $PPID') ; echo $p
  echo -n 'p=$(exec sh -c '\''echo $PPID'\'') = '; p=$(exec sh -c 'echo $PPID') ; echo $p
}
pids
pids | cat
echo -e "$(pids)"

und seine Ausgabe

------
bash(5975,5975)---pidtest(13474,13474)---pstree(13475,13474)
PPID =  5975
$$ =  13474
BASHPID = 13474
sh -c echo $PPID = 13474
$(sh -c 'echo $PPID') = 13474
$(exec sh -c 'echo $PPID') = 13474
p=$(sh -c 'echo $PPID') = 13474
p=$(exec sh -c 'echo $PPID') = 13474
------
bash(5975,5975)---pidtest(13474,13474)-+-cat(13482,13474)
                                       `-pidtest(13481,13474)---pstree(13483,13474)
PPID =  5975
$$ =  13474
BASHPID = 13481
sh -c echo $PPID = 13481
$(sh -c 'echo $PPID') = 13481
$(exec sh -c 'echo $PPID') = 13481
p=$(sh -c 'echo $PPID') = 13481
p=$(exec sh -c 'echo $PPID') = 13481
------
bash(5975,5975)---pidtest(13474,13474)---pidtest(13489,13474)---pstree(13490,13474)
PPID =  5975
$$ =  13474
BASHPID = 13489
sh -c echo $PPID = 13489
$(sh -c 'echo $PPID') = 13492
$(exec sh -c 'echo $PPID') = 13489
p=$(sh -c 'echo $PPID') = 13495
p=$(exec sh -c 'echo $PPID') = 13489

Ersetzen Sie Ihre Lieblingsschale im shebang: sh, bash, mksh, ksh, etc ...

Ich verstehe nicht, warum die Fälle 2 und 3 unterschiedliche Ergebnisse liefern oder warum sich die Ergebnisse für Fall 3 zwischen den Schalen unterscheiden. Ich habe versucht bash, kshund mkshauf Arch Linux FWIW.

Sternenhimmel
quelle
0
#!/bin/ksh
 function os_python_pid {
/usr/bin/python  -c 'import os,sys ; sys.stdout.write(str(os.getppid()))'
}
function os_perl_pid {
/usr/bin/perl -e 'print getppid'
}

echo "I am $$"
    echo "I am $(os_python_pid) :: $(os_perl_pid)"
function proce {
  sleep 3
  echo "$1 :: $(os_python_pid) :: $(os_perl_pid)"
}

for x in aa bb cc; do
  proce $x &
  echo "Started: $!"
done
Alty
quelle