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.

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

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

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

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.


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.