Como obter o PID do subshell no Korn Shell (equivalente a $ BASHPID)

8

No bash, você tem esta variável útil: $ BASHPID, que sempre retorna o PID do subshell em execução no momento. Como posso obter o PID de um subshell em ksh? Por exemplo, veja o código abaixo:

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

Isso gera o seguinte:

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

O que eu quero é a linha que começa com ****a saída do PID do subshell, no caso do exemplo que seria 5329.

Patkos Csaba
fonte

Respostas:

10

Eu não acho que esteja disponível no ksh. Existe uma solução POSIX que envolve a execução de um processo externo:

sh -c 'echo $PPID'

No Linux, readlink /proc/selftambém funcionaria, mas não vejo nenhuma vantagem (pode ser um pouco mais rápida; pode ser útil em uma variante do BusyBox que possui, readlinkmas não possui $PPID, mas acho que não existe).

Observe que, para obter o valor no shell, você precisa ter cuidado para não executar esse comando em um subconcha de curta duração. Por exemplo, p=$(sh -c 'echo $PPID')pode mostrar a saída do subshell que é chamado shdentro da substituição de comando (ou pode não ser, alguns shells otimizam esse caso). Em vez disso, execute

p=$(exec sh -c 'echo $PPID')
Gilles 'SO- parar de ser mau'
fonte
Eu já vi essa sugestão, mas não está funcionando. Isso me dá um terceiro PID ... no entanto, testarei novamente segunda-feira quando voltar ao trabalho.
Patkos Csaba
@PatkosCsaba No ksh, provavelmente funcionaria porque o ksh otimiza os garfos, mas em alguns outros shells, como o bash, você precisa ter cuidado para não receber acidentalmente o pid de um subconcha. Veja minha resposta atualizada.
Gilles 'SO- stop be evil'
Isso está funcionando: $(exec sh -c 'echo $PPID')No entanto, o comando simples inicial sh -c 'echo $PPID'fornece um terceiro PID. Então, obrigado pela solução. Aceitaram.
Patkos Csaba
1
@FranklinYu Isso ocorre porque nenhum dos dois está criando um subshell. O Bash otimiza (sh -c 'echo $PPID')para evitar a criação de um subshell. Contraste com (sh -c 'echo $PPID'; true). Essa otimização só entra em ação se você tentar acessar $BASHPIDcomo a última coisa antes que o subshell saia, ou seja, apenas nos casos em que você não pode fazer nada com o valor. Portanto, na prática, você pode substituir $BASHPIDpor $(sh -c 'echo $PPID').
Gilles 'SO- stop being evil'
1
@FranklinYu Não acho que exista algo incorporado. É claro que você pode usar o método portátil sh -c 'echo $PPID'.
Gilles 'SO- stop be evil'
3

Você pode conseguir o que deseja, mas precisa colocar run_something em um script separado. Não sei exatamente por que, mas $$ não é reavaliado quando usado em uma função no mesmo script que está sendo chamado. Eu acho que o valor de $$ é atribuído uma vez depois que o script é analisado e antes de ser executado.

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;

resultado

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
fonte
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
fonte
Dois nitpicks: if [[ ${!KSH_VERSION} = .sh.version ]]; then(apenas um =) e elif [[ -z ${BASHPID+_} ]]; then(evite usar o implícito -nentre colchetes duplos, o pdksh antigo não sabia disso).
mirabilos
0

Seguindo a resposta de @Gilles que encontrei enquanto resolvia outro problema que tive, criei um programa de teste rápido que sustenta a teoria de que a resposta correta é:

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

Descobri que há momentos em que execnão é necessário, mas confirmei que usá-lo é a única maneira de obter o pid correto o tempo todo em todas as conchas que tentei. Aqui está o meu teste:

#!/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 sua saída

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

Substitua seu shell favorito no shebang: sh, bash, mksh, ksh, etc ...

Não entendo por que os casos 2 e 3 apresentam resultados diferentes, nem por que os resultados do caso 3 diferem entre as conchas. Eu tentei bash, kshe mkshno Arch Linux FWIW.

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