Bagaimana saya bisa mendapatkan pid dari sebuah subkulit?

13

Bagaimana saya bisa mendapatkan pid dari sebuah subkulit?

Sebagai contoh:

$ echo $$
16808

Ini tidak berfungsi, karena shell asli mengembang $$:

$ ( echo $$ )
16808

Mengapa kutip tunggal tidak berfungsi? Setelah shell asli menghapus kutipan tunggal, apakah subshell tidak berkembang $$dengan sendirinya?

$ ( echo '$$' )
$$

Mengapa evaltidak berhasil juga? Apakah evaldijalankan oleh subkulit? Mengapa itu memberi saya PID shell asli?

$ ( eval echo '$$' )
16808

Terima kasih.

Tim
sumber
Saya menyarankan membuka kembali, karena pertanyaan-pertanyaan pada dasarnya berbeda menurut saya ("bagaimana menghindari $$ekspansi" vs "pid yang berbeda dalam subkulit").
peterh

Jawaban:

12

Selain bash's $BASHPID, Anda dapat melakukannya portable dengan:

pid=$(exec sh -c 'echo "$PPID"')

Contoh:

(pid=$(exec sh -c 'echo "$PPID"'); echo "$$ $pid")

Anda dapat membuatnya menjadi fungsi:

# usage getpid [varname]
getpid(){
    pid=$(exec sh -c 'echo "$PPID"')
    test "$1" && eval "$1=\$pid"
}

Perhatikan bahwa beberapa shell (mis. zshAtau ksh93) JANGAN memulai subproses untuk setiap subkulit yang dibuat dengan (...); dalam hal ini, $pidmungkin berakhir menjadi sama dengan $$, yang tepat, karena itu PID dari proses getpiddipanggil.

mosvy
sumber
1
Tidak. Tapi tolong jangan berasumsi bahwa subshell harus dijalankan dalam subproses - itu bukan kasusnya ksh93, misalnya.
Mosvy
1
Ini akan bekerja dengan baik di ksh93 - itu akan selalu mengembalikan pid dari proses itu dipanggil. Ini adalah (...)dari contoh yang mungkin tidak menelurkan proses terpisah, seperti halnya di bash.
Mosvy
1
Juga, beberapa shell menyukai zshatau yashmengoptimalkan fork()perintah terakhir dalam subkulit. Mereka bahkan dapat mengoptimalkan garpu untuk subkulit jika itu adalah perintah terakhir dalam skrip sehingga Anda getpidbahkan dapat melaporkan induknya $$. Anda dapat mendefinisikan getpidsebagai: getpid(){ sh -c 'echo "$PPID"'; return; }untuk menonaktifkan menghindari masalah.
Stéphane Chazelas
1
@ HaroldFischer 1. tanpa execatau tanpa optimasi itu, sh -c ...proses akan menjadi cucu, bukan anak dari proses di mana $(...)substitusi perintah digunakan, dan $PPIDakan menjadi pid dari $(...)subkulit. Itulah yang terjadi pada contoh set -E+ trap ERRbash di atas.
Mosvy
1
@ HaroldFischer 2. test "$1"menguji apakah $1string kosong atau tidak - cara cepat dan kotor untuk menguji apakah fungsi itu diberikan varnameargumen untuk menetapkan pid ke atau tidak; menggunakan fungsi bukanlah ide paling cemerlang di tempat pertama.
Mosvy
18
$ echo $BASHPID
37152
$ ( echo $BASHPID )
18633

Dari manual:

BASHPID

Memperluas ID proses dari proses bash saat ini. Ini berbeda dari $$dalam keadaan tertentu, seperti subkulit yang tidak memerlukan bash untuk diinisialisasi ulang.

$

Memperluas ID proses dari shell. Dalam sebuah ()subkulit, itu mengembang ke ID proses shell saat ini, bukan subkulit.

Terkait:

Kusalananda
sumber
Terima kasih. (1) Apa yang dimaksud dengan "inisialisasi ulang"? (2) Bisakah Anda juga mempertimbangkan mengapa cara-cara yang saya coba tidak berhasil?
Tim
@Tim saya yakin ini dijawab oleh Gilles di sini . Bash tidak memperbarui $$dalam subshell.
Kusalananda
Apakah maksud Anda saya harus selalu menggunakan $ BASHPID sebagai pengganti $$ dalam kasus apa pun di bash? Kapan saya harus menggunakan yang mana?
Tim
@Tim Tergantung pada apakah Anda, dalam subkulit, ingin mendapatkan ID proses skrip atau dari subkulit. Kedua kemungkinan disediakan dan mana yang benar tergantung pada aplikasi. Tidak ada jawaban yang lebih spesifik untuk itu.
Kusalananda
1
@Tim PID shell induk dari subkulit tidak dapat ditemukan secara andal kecuali Anda mengatur untuk menyimpan $BASHPIDdalam variabel dan menggunakannya dalam subkulit. Memang ada $PPID, tapi itu PID induk dari shell dalam arti yang $$sama dengan PID shell (tidak diatur ulang dalam subkulit). Tidak ada $BASHPPIDvariabel.
Kusalananda