Comment obtenir le PID du sous-shell dans Korn Shell (équivalent de $ BASHPID)

8

En bash, vous avez cette variable pratique: $ BASHPID qui retourne toujours le PID du sous-shell en cours d'exécution. Comment puis-je obtenir le PID d'un sous-shell en ksh? Par exemple, voyez le code ci-dessous:

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

Cela génère les éléments suivants:

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

Ce que je veux, c'est la ligne commençant par ****sortir le PID du sous-shell, dans le cas de l'exemple qui serait 5329.

Patkos Csaba
la source

Réponses:

10

Je ne pense pas que ce soit disponible dans ksh. Il existe une solution POSIX qui implique l'exécution d'un processus externe:

sh -c 'echo $PPID'

Sous Linux, readlink /proc/selfcela fonctionnerait aussi, mais je ne vois aucun avantage (il pourrait être légèrement plus rapide; il pourrait être utile sur une variante BusyBox qui l'a readlinkmais pas $PPID, mais je ne pense pas qu'il y en ait un).

Notez que pour obtenir la valeur dans le shell, vous devez faire attention à ne pas exécuter cette commande dans un sous-sous-shell de courte durée. Par exemple, p=$(sh -c 'echo $PPID')peut afficher la sortie du sous-shell qui invoque shdans la substitution de commandes (ou il se peut que ce ne soit pas le cas, certains shells optimisent ce cas). Au lieu de cela, exécutez

p=$(exec sh -c 'echo $PPID')
Gilles 'SO- arrête d'être méchant'
la source
J'ai déjà vu cette suggestion, mais elle ne fonctionne pas. Cela me donne un troisième PID ... mais je le testerai à nouveau lundi quand je serai de retour au travail.
Patkos Csaba,
@PatkosCsaba Dans ksh, cela fonctionnerait probablement parce que ksh optimise les fourches, mais dans certains autres shells tels que bash, vous devez faire attention à ne pas obtenir accidentellement le pid d'un sous-sous-shell. Voir ma réponse mise à jour.
Gilles 'SO- arrête d'être méchant'
Cela fonctionne: $(exec sh -c 'echo $PPID')Cependant, la commande simple initiale sh -c 'echo $PPID'donne un troisième PID. Merci donc pour la solution. Accepté.
Patkos Csaba
1
@FranklinYu C'est parce que ni l'un ni l'autre ne crée un sous-shell. Bash optimise (sh -c 'echo $PPID')pour éviter de créer un sous-shell. Contraste avec (sh -c 'echo $PPID'; true). Cette optimisation n'intervient que si vous essayez d'accéder $BASHPIDcomme la dernière chose avant la fin du sous-shell, c'est-à-dire uniquement dans les cas où vous ne pouvez rien faire avec la valeur. Donc, en pratique, vous pouvez remplacer $BASHPIDpar $(sh -c 'echo $PPID').
Gilles 'SO- arrête d'être méchant'
1
@FranklinYu Je ne pense pas qu'il y ait quelque chose de intégré. Vous pouvez bien sûr utiliser la méthode portable sh -c 'echo $PPID'.
Gilles 'SO- arrête d'être méchant'
3

Vous pouvez réaliser ce que vous voulez, mais vous devez mettre run_something dans un script séparé. Je ne sais pas exactement pourquoi, mais $$ n'est pas réévalué lorsqu'il est utilisé dans une fonction du même script qui l'appelle. Je suppose que la valeur de $$ est attribuée une fois après l'analyse du script et avant son exécution.

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;

production

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
la source
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
la source
Deux nitpicks: if [[ ${!KSH_VERSION} = .sh.version ]]; then(un seul =) et elif [[ -z ${BASHPID+_} ]]; then(éviter d'utiliser l'implicite -nentre crochets doubles, l'ancien pdksh ne le savait pas).
mirabilos
0

Suite à la réponse de @Gilles que j'ai rencontrée tout en résolvant un autre problème que j'avais, j'ai élaboré un programme de test rapide qui sous-tend la théorie selon laquelle la bonne réponse est:

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

J'ai trouvé qu'il y a des moments où ce execn'est pas nécessaire mais j'ai confirmé que son utilisation est le seul moyen d'obtenir le bon pid tout le temps dans tous les shells que j'ai essayés. Voici mon 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)"

et sa sortie

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

Remplacez votre coque préférée dans le tralala: sh, bash, mksh, ksh, etc ...

Je ne comprends pas pourquoi les cas 2 et 3 donnent des résultats différents, ni pourquoi les résultats du cas 3 diffèrent entre les obus. J'ai essayé bash, kshet mkshsur Arch Linux FWIW.

étoilé
la source
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
la source