Cara mendapatkan PID subkulit di Korn Shell (setara dengan $ BASHPID)

8

Dalam bash, Anda memiliki variabel praktis ini: $ BASHPID yang selalu mengembalikan PID subshell yang saat ini berjalan. Bagaimana saya bisa mendapatkan PID subkulit dalam ksh? Misalnya lihat kode di bawah ini:

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

Ini menghasilkan yang berikut:

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

Apa yang saya inginkan adalah baris yang dimulai dengan ****menampilkan PID subshell, dalam kasus contoh yang akan menjadi 5329.

Patkos Csaba
sumber

Jawaban:

10

Saya rasa itu tidak tersedia di ksh. Ada solusi POSIX yang melibatkan menjalankan proses eksternal:

sh -c 'echo $PPID'

Di Linux, readlink /proc/selfjuga akan berfungsi, tetapi saya gagal melihat keuntungan apa pun (mungkin sedikit lebih cepat; ini bisa berguna pada varian BusyBox yang telah readlinktetapi tidak $PPID, tetapi saya tidak berpikir ada satu).

Perhatikan bahwa untuk mendapatkan nilai dalam shell, Anda harus berhati-hati untuk tidak menjalankan perintah itu dalam sub-sub-shell yang berumur pendek. Sebagai contoh, p=$(sh -c 'echo $PPID')mungkin memperlihatkan output dari subkulit yang memanggil shdalam substitusi perintah (atau mungkin tidak, beberapa shell mengoptimalkan kasing itu). Sebaliknya, jalankan

p=$(exec sh -c 'echo $PPID')
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Saya sudah melihat saran ini tetapi tidak berfungsi. Ini memberi saya PID ketiga ... namun saya akan mengujinya lagi Senin ketika saya kembali bekerja.
Patkos Csaba
@ PatosCsaba Dalam ksh mungkin akan berhasil karena ksh mengoptimalkan garpu, tetapi di beberapa shell lain seperti bash Anda harus berhati-hati untuk tidak sengaja mendapatkan pid dari sub-sub-shell. Lihat jawaban saya yang diperbarui.
Gilles 'SO- berhenti bersikap jahat'
Ini bekerja: $(exec sh -c 'echo $PPID')Namun perintah sederhana awal sh -c 'echo $PPID'memberikan PID ketiga. Jadi terima kasih atas solusinya. Diterima
Patkos Csaba
1
@ FranklinYu Itu karena tidak ada yang membuat subkulit. Bash mengoptimalkan (sh -c 'echo $PPID')untuk menghindari membuat subkulit. Berbeda dengan (sh -c 'echo $PPID'; true). Pengoptimalan ini hanya berlaku jika Anda mencoba mengakses $BASHPIDsebagai hal terakhir sebelum subkulit keluar, yaitu hanya dalam kasus di mana Anda tidak dapat melakukan apa pun dengan nilai tersebut. Jadi dalam praktiknya, Anda bisa menggantinya $BASHPIDdengan $(sh -c 'echo $PPID').
Gilles 'SANGAT berhenti menjadi jahat'
1
@ FranklinYu Saya tidak berpikir ada sesuatu di dalamnya. Anda tentu saja dapat menggunakan metode portabel sh -c 'echo $PPID'.
Gilles 'SANGAT berhenti menjadi jahat'
3

Anda dapat mencapai apa yang Anda inginkan, tetapi Anda harus meletakkan run_something ke dalam skrip terpisah. Saya tidak begitu yakin mengapa, tetapi $$ tidak dievaluasi kembali ketika digunakan dalam fungsi dalam skrip yang sama yang memanggilnya. Saya kira nilai $$ diberikan setelah skrip diuraikan dan sebelum dijalankan.

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;

keluaran

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
sumber
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
sumber
Dua nitpicks: if [[ ${!KSH_VERSION} = .sh.version ]]; then(hanya satu =) dan elif [[ -z ${BASHPID+_} ]]; then(hindari menggunakan implisit -ndalam tanda kurung ganda, pdksh lama tidak mengetahuinya).
mirabilos
0

Mengikuti jawaban oleh @Gilles yang saya temui ketika sedang memecahkan masalah lain yang saya miliki, saya mengumpulkan program tes cepat yang mendukung teori bahwa jawaban yang benar adalah:

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

Saya menemukan ada saat-saat ketika exectidak diperlukan tetapi saya mengkonfirmasi bahwa menggunakannya adalah satu-satunya cara untuk mendapatkan pid yang benar sepanjang waktu di semua kerang yang saya coba. Ini tes saya:

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

dan hasilnya

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

Pengganti shell favorit Anda dalam peristiwa tersebut: sh, bash, mksh, ksh, dll ...

Saya tidak mengerti mengapa case 2 dan 3 memberikan hasil yang berbeda, atau mengapa hasil untuk case 3 berbeda antara shell. Saya mencoba bash, kshdan mkshpada Arch Linux FWIW.

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