Dapatkan PID dari perintah apa pun dalam urutan perintah pipa yang di-background

11

Jika, dalam bash, saya mengeksekusi:

cmd1 | cmd2 | ... | cmdi | ... | cmdn &

di mana cmd{1..n}mungkin tidak berbeda, bagaimana cara mendapatkan PID cmdi? Atau, bagaimana saya bisa menandai cmdiproses? (Misalnya, kirimkan SIGUSR1?) pkill/ pgrep, pidofDll. Tidak terlihat seperti jawaban yang baik, karena contoh lain cmdimungkin berjalan, termasuk sebagai bagian dari pipa yang sama. jobs -pmemberikan PID cmd1untuk saya.

ibisa apa saja {1..n}.

muru
sumber
duplikat yang mungkin dari Bagaimana saya bisa mendapatkan pid dari suatu proses dimulai dengan cara ini
G-Man Mengatakan 'Reinstate Monica'
1
@ G-Man Care menjelaskan? Saya hanya melihat kesamaan dangkal, dan seperti yang saya jelaskan dalam jawaban Ramesh, memodifikasi set perintah tidak banyak digunakan.
muru
Kesamaan yang dangkal? cat /var/run/out | nc -l 8080hanya terlihat serupa dengan cmd1 | cmd2? Kendala Anda, bahwa Anda ingin mengetikkan pipa telanjang dan kemudian memulihkan PID, adalah (1) tidak disebutkan dalam pertanyaan, dan (2) tidak memungkinkan untuk solusi umum yang baik.
G-Man Mengatakan 'Reinstate Monica'
@ G-Man Sebaliknya, Anda memaksakan kendala yang tidak dinyatakan sederhana. cmd1 | cmd2adalah kasus yang sangat khusus di mana kedua PID mudah diperoleh. Apakah saya mengatakan sesuatu tentang n? Jadi mengapa Anda mengasumsikan n = 2? Apakah saya mengatakan sesuatu tentang cmdi? Jadi mengapa Anda menganggap saya bisa memodifikasi cmdi? Saya meminta solusi umum dan Anda memberlakukan batasan.
muru

Jawaban:

6

Untuk versi asli dari pertanyaan, ketika hanya PID perintah terakhir yang diinginkan, variabel khusus $!sempurna.

foo | bar | baz &
baz_pid=$!

Tidak ada akses mudah serupa ke PID dari proses lain.

Butuh waktu lama untuk $pipestatus(zsh) dan $PIPESTATUS(bash) untuk ditambahkan, akhirnya memberi kita akses ke semua status keluar dalam pipa, selain $?untuk yang terakhir yang sudah ada sejak shell Bourne asli. Mungkin sesuatu yang analog akan terjadi pada $!akhirnya.


sumber
Apakah Anda keberatan jika saya mengedit pertanyaan untuk meminta PID dari perintah sewenang-wenang dalam daftar juga? Atau haruskah saya memulai pertanyaan baru?
muru
Anda mungkin harus menunggu lebih lama untuk jawaban atas pertanyaan itu. Saya tidak memiliki perasaan yang kuat tentang organisasi situs stackexchange, jadi pisahkan pertanyaan, edit pertanyaan, apa pun ... tidak akan mengganggu saya
Tidak apa-apa, masalah langsung diselesaikan, sekarang rasa ingin tahu yang bertanggung jawab. Saya akan mengeditnya. Hanya kepala-up, karena saya telah melihat pertanyaan berubah secara drastis dan meninggalkan jawaban yang lebih tua tampak sangat tidak pada tempatnya.
muru
@muru - perhatikan lebih baik membuatnya menjadi Q baru yang merujuk pada yang ini.
slm
@slm sepatutnya dicatat. Akan melakukannya di masa depan.
muru
4

Saya pikir Anda bisa melakukan sesuatu seperti yang disarankan di sini .

(ls -l | echo "Hello" | df -h & echo $! >&3 ) 3>pid

Di sini, dalam contoh di atas, saya telah mengambil pid dari proses pipa ketiga dan mencatatnya ke file pid. Saya bisa mencatatnya untuk setiap proses pipa.

Ramesh
sumber
Menarik, tetapi ini akan melibatkan memodifikasi set perintah. Tidak banyak digunakan setelah perintah dijalankan.
muru
@muru - apa? apa gunanya setiap PID setelah selesai dieksekusi? Anda ingin PID pipa? jobs -p. memberi sinyal dengan SIGPIPE. Apakah kamu mau cmdi- ini.
mikeserv
1
@ mikeserv Tidak jika mereka di latar belakang, berjalan seperti yang kita bicarakan. Dengan sihir apa aku harus memodifikasi baris perintah untuk itu?
muru
1
@muru itu akan menjadi sihir. Anda membutuhkan debugger.
mikeserv
Saya menemukan ini menjadi pola yang berguna untuk memulai proses latar belakang, menunggu mereka untuk mencapai suatu keadaan, dan kemudian membunuh mereka. Jika ada yang tertarik: gist.github.com/MatrixManAtYrService/…
MatrixManAtYrService
2

Solusi khusus Linux yang tidak terlalu portabel adalah melacak proses menggunakan pipa yang menghubungkannya. Kita bisa mendapatkan PID dari perintah pertama ( jobs -p) dan terakhir ( $!) di dalam pipa. Menggunakan salah satu PID, skrip ini dapat melakukan pekerjaan:

#! /bin/bash

PROC=$1
echo $PROC

if [[ $(readlink /proc/$PROC/fd/1) =~ ^pipe: ]]
then
    # Assuming first process in chain...
    NEXT_FD=1
elif [[ $(readlink /proc/$PROC/fd/0) =~ ^pipe: ]]
then
    # Last process in chain...
    NEXT_FD=0
else
    # Doesn't look like a pipe.
    exit
fi

NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)

while [[ $NEXT_PROC_PIPE =~ ^pipe: ]] 
do
    PROC=$(find /proc/*/fd -type l -printf "%p/%l\n" 2>/dev/null | awk -F'/' '($6 == "'"$NEXT_PROC_PIPE"'") && ($3 != "'$PROC'" ) {print $3}')
    NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)
    echo $PROC
done
muru
sumber
Untuk yang penasaran, ada lebih banyak tentang hal semacam ini di sini: unix.stackexchange.com/a/486233/146169
MatrixManAtYrService
0

Saya menggunakan array berbasis nol di sini dalam kode ini. Berhati-hatilah dengan apa yang Anda jalankan eval.

#!/bin/bash

cmd=('sleep 10' 'sleep 2' 'sleep 5')
first=1
for c in "${cmd[@]}"; do
  ((first)) && { pipe=$c; first=0; } || pipe+='|'$c
done
shopt -u lastpipe
eval $pipe &

printf 'Pipe:\n%s\n\n' "$pipe"

shellpid=$BASHPID
parent=$(ps -o pid= --ppid $shellpid | head -n -1)
declare -a pids=()
mapfile -t pids < <(printf '%s\n' $(ps -o pid= --ppid $parent))
printf '%s\n' 'Listing the arrays:'
printf '%2s %6s %s\n' i PID command
for i in "${!cmd[@]}"; do
    printf '%2d %6d %s\n' "$i" "${pids[i]}" "${cmd[i]}"
done

printf '\n%s\n' 'ps listing:'
ps xao pid,ppid,command | head -n 1
ps xao pid,ppid,command | tail | head -n -3
jarno
sumber