Come ottenere il PID della subshell in Korn Shell (equivalente di $ BASHPID)


8

In bash hai questa utile variabile: $ BASHPID che restituisce sempre il PID della subshell attualmente in esecuzione. Come posso ottenere un PID di una subshell in ksh? Ad esempio vedi il codice qui sotto:

#!/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 $!"

Ciò genera quanto segue:

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

Quello che voglio è la linea che inizia con ****l'output del PID della subshell, nel caso dell'esempio che sarebbe 5329.

Risposte:


10

Non credo sia disponibile in ksh. Esiste una soluzione POSIX che prevede l'esecuzione di un processo esterno:

sh -c 'echo $PPID'

Su Linux, readlink /proc/selffunzionerebbe anche, ma non vedo alcun vantaggio (potrebbe essere leggermente più veloce; potrebbe essere utile su una variante BusyBox che ha readlinkma non lo è $PPID, ma non credo che ce ne sia uno).

Si noti che per ottenere il valore nella shell, è necessario fare attenzione a non eseguire quel comando in una sub-shell di breve durata. Ad esempio, p=$(sh -c 'echo $PPID')potrebbe mostrare l'output della subshell che richiama shall'interno della sostituzione del comando (oppure potrebbe non essere, alcune shell ottimizzano quel caso). Invece, corri

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

Ho già visto questo suggerimento ma non funziona. Mi dà un terzo PID ... comunque lo proverò di nuovo lunedì quando torno al lavoro.
Patkos Csaba,

@PatkosCsaba In ksh probabilmente funzionerebbe perché ksh ottimizza le forcelle, ma in alcune altre shell come bash è necessario fare attenzione a non ottenere accidentalmente il pid di una sub-sub-shell. Vedi la mia risposta aggiornata.
Gilles 'SO- smetti di essere malvagio' il

Questo funziona: $(exec sh -c 'echo $PPID')tuttavia il comando semplice iniziale sh -c 'echo $PPID'fornisce un terzo PID. Quindi grazie per la soluzione. Accettato.
Patkos Csaba,

1
@FranklinYu Questo perché nessuno dei due sta creando una subshell. Bash ottimizza (sh -c 'echo $PPID')per evitare la creazione di una subshell. In contrasto con (sh -c 'echo $PPID'; true). Questa ottimizzazione si attiva solo se si tenta di accedere $BASHPIDcome ultima cosa prima che esca la subshell, vale a dire solo nei casi in cui non è possibile eseguire alcuna operazione con il valore. Quindi, in pratica, puoi sostituirlo $BASHPIDcon $(sh -c 'echo $PPID').
Gilles 'SO- smetti di essere malvagio' il

1
@FranklinYu Non credo ci sia niente incorporato. Puoi ovviamente usare il metodo portatile sh -c 'echo $PPID'.
Gilles 'SO- smetti di essere malvagio' il

3

Puoi ottenere ciò che desideri, ma devi inserire run_something in uno script separato. Non sono esattamente sicuro del perché, ma $$ non viene rivalutato quando viene utilizzato in una funzione nello stesso script che lo chiama. Immagino che il valore di $$ sia assegnato una volta dopo aver analizzato lo script e prima che venga eseguito.

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;

produzione

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

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

Due nitpick: if [[ ${!KSH_VERSION} = .sh.version ]]; then(solo uno =) e elif [[ -z ${BASHPID+_} ]]; then(evitare di usare l'implicito -ntra parentesi quadre, il vecchio pdksh non lo sapeva).
mirabilos,

0

Seguendo la risposta di @Gilles che ho riscontrato mentre risolvo un altro problema che ho avuto, ho messo insieme un programma di test rapido che sostiene la teoria che la risposta corretta è:

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

Ho scoperto che ci sono momenti in cui execnon è richiesto, ma ho confermato che usarlo è l'unico modo per ottenere il pid corretto per tutto il tempo in tutte le shell che ho provato. Ecco il mio 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)"

e il suo output

------
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

Sostituire la shell preferita nella baracca: sh, bash, mksh, ksh, ecc ...

Non capisco perché i casi 2 e 3 danno risultati diversi, né perché i risultati per il caso 3 differiscono tra shell. Ho provato bash, kshe mkshsu Arch Linux FWIW.


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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.