Bash, bagaimana cara membiarkan beberapa proses latar belakang berjalan tetapi menunggu yang lain?

11

Saya punya (belum) lain wait, &, &&pertanyaan aliran kontrol ..

Katakanlah saya memiliki skrip seperti ini di mana saya ingin melakukan sebanyak mungkin pekerjaan pada saat yang bersamaan:

# may take some hours
something InputA > IrrelevantA &
something InputB > IrrelevantB &

# may take an hour
(
   somethingElse InputA > OutputA &
   somethingElse InputB > OutputB &
)&& combine OutputA OutputB > Result

...morestuff

Pertanyaan 1: Dalam skrip, apakah combinemenunggu kedua somethingElseproses selesai sementara kedua somethingproses berlanjut?

Pertanyaan 2: Jika tidak - dan saya kira tidak - bagaimana saya bisa combinemenunggu hanya untuk kedua somethingElseproses sementara somethingproses di atas terus berjalan di latar belakang?

Stephen Henderson
sumber

Jawaban:

13

Dalam contoh Anda, combineperintah hanya akan dijalankan segera setelah subshell keluar (dan asalkan proses latar belakang terakhir dimulai tanpa kesalahan). Subshell akan keluar segera setelah pekerjaan dimulai karena tidak ada waitperintah.

Jika Anda ingin menjalankan perintah berdasarkan nilai balik dua atau lebih proses latar belakang simultan, maka saya tidak bisa melihat cara lain selain menggunakan file sementara untuk nilai kembali. Ini karena waithanya dapat mengembalikan nilai pengembalian dari salah satu proses yang ditunggu. Juga karena proses latar belakang harus dijalankan dalam subkulit untuk mendapatkan nilai kembali sama sekali, mereka tidak dapat disimpan dalam variabel. Anda bisa melakukannya:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

tmp1=$(mktemp)
tmp2=$(mktemp)

( somethingElse InputA >OutputA; echo $? >"$tmp1" ) &
proc1=$!

( somethingElse InputB >OutputB; echo $? >"$tmp2" ) &
proc2=$!

wait "$proc1" "$proc2"

read ret1 <"$tmp1"
read ret2 <"$tmp2"
[ "$ret1" = 0 && "ret2" = 0 ] && combine OutputA OutputB >Result

rm "$tmp1" "$tmp2"

Jika Anda tidak terlalu peduli dengan nilai pengembalian, Anda bisa memulai pekerjaan secara normal dan menggunakan wait:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

somethingElse InputA >OutputA &
proc1=$!

somethingElse InputB >OutputB &
proc2=$!

wait "$proc1" "$proc2"
combine OutputA OutputB >Result
Graeme
sumber
Hai, saya pikir opsi ke-2 akan bekerja untuk saya ...
Stephen Henderson
3

Apakah proses substitusi akan lebih efisien, terutama jika Anda tidak perlu menyimpan file OutputAdan OutputB, dan hanya peduli Result? Apakah ini akan sangat menghemat waktu karena jika Anda memiliki I / O yang lambat dalam menulis ke disk, menyimpan file OutputAdan OutputBmungkin langkah membatasi tingkat?

combine  <(somethingElse InputA)  <(somethingElse InputB)  >  Result

Substitusi proses memungkinkan Anda untuk menempatkan perintah di dalam <(..here..)alih-alih menyimpan output ke file, dan kemudian membacanya sebagai input dalam langkah "menggabungkan".

Jika ingatan adalah batasan, dan ukuran outputAserta outputBlebih dari apa yang dapat dipegang ingatan, akankah ia mengalahkan seluruh tujuan?

Akankah combinemenunggu sampai kedua proses selesai sebelum mulai berjalan?

TW Tan
sumber
Ini bukan "Jeopardy"; silakan lakukan tidak frase tanggapan Anda dalam bentuk pertanyaan. Serius, Anda punya ide baru, dan saya pikir itu cukup bagus. Untuk menanggapi beberapa poin Anda: combineakan mulai berjalan segera setelah kedua somethingElseperintah telah dimulai, tetapi tidak apa-apa, karena <(…)semuanya pipa; jadi combinehanya akan dipaksa untuk menunggu data jika somethingElseprosesnya lebih cepat. Dan, karena itu pipa, ukuran bukan masalah. … (Lanjutan)
G-Man Mengatakan 'Reinstate Monica'
(Lanjutan) ... Satu-satunya masalah substantif yang saya miliki dengan jawaban Anda adalah bahwa itu tidak memungkinkan untuk menguji status keluar dari somethingElseproses - dan itu tidak sepenuhnya jelas apakah itu penting bagi penanya. Tetapi, juga, sebuah jawaban seharusnya tidak menanyakan pertanyaan seperti itu.
G-Man Mengatakan 'Reinstate Monica'
2

Anda dapat menggunakan waitperintah:

(echo starting & sleep 10 & wait) && echo done

Anda dapat melihat garis "mulai" terjadi segera, dan "selesai" menunggu selama 10 detik.

psusi
sumber
umumnya menunggu membutuhkan proses anak dari shell yang sama. Tunggu di sana cukup rumit.
mikeserv
1
@ mikeserv, apa yang kamu bicarakan? Itulah intinya: ia menunggu semua anak dalam subkulit itu.
psusi
oleh tes awal saya ini berfungsi. Saya akan mencobanya pada naskah besar sekarang
Stephen Henderson
Tepat - anak-anak dari cangkang yang sama - cangkang sub . Ini harus bekerja untuk setiap proses yang tidak berusaha untuk melarikan diri - atau mengubah bentuk atau apa pun. Itulah yang saya maksudkan - selama proses Anda menghormati pemimpin proses menunggu tidak apa-apa, tetapi begitu suatu proses mencoba untuk menjadi pemimpin prosesnya sendiri, menunggu akan memiliki masalah.
mikeserv
0

Saya benar-benar menunjukkan dengan tepat bagaimana hal semacam ini dapat dilakukan dalam jawaban lain di sini . Jawaban itu adalah pertanyaan tentang memastikan 2 log dikelola oleh proses latar belakang, jadi saya menunjukkannya dengan 10.

Demo Script

cat <<-\DEMO >|${s=/tmp/script} 
printf 'tty is %s\nparent pid is %s\npid is pid=%s\n' \
     "$(tty)" "$PPID" "$$"
exec 1>&2 ; nums=$(seq 0 9)
rm ${files=$(printf "/tmp/file%s\n" $nums)}
for n in $nums ; do { for f in $files ; do
    echo "Line $n" >>"$f" ; done
sleep 1 ; } ; done
#END
DEMO

Jalankan Demo

s=/tmp/script ;chmod +x $s ;info="$(($s &)2>&- &)"
echo "$info" ; pid="${info##*=}" ; echo
while ps -p $pid >/dev/null ; do sleep 3 ; done
for f in /tmp/file[0-9] ; do
    printf 'path : %s\tline count : %s\n' \
        $f $(<$f wc -l)
done

Keluaran:

tty is not a tty
parent pid is 1
pid is 12123

path : /tmp/file0    line count : 10
path : /tmp/file1    line count : 10
path : /tmp/file2    line count : 10
path : /tmp/file3    line count : 10
path : /tmp/file4    line count : 10
path : /tmp/file5    line count : 10
path : /tmp/file6    line count : 10
path : /tmp/file7    line count : 10
path : /tmp/file8    line count : 10
path : /tmp/file9    line count : 10

Di atas menunjukkan. Itu membangun dan menjalankan skrip bernama /tmp/script, chmod's sebagai executable, dan berjalan itu dalam &backgroundsebuah &backgrounded ( subshell ).

Script rms /tmp/file0-910 file dan echoessatu baris setiap detik menjadi 10 file . Saya menangkap beberapa $infodari proses pembatalan dan mempresentasikannya melalui $(command substitution). While pslaporan masih pada $pidpenangkapan saya, saya tahu itu masih berjalan jadi saya sleep.Ketika selesai, garis-garis di semua 10 file dihitung denganwc.

Setelah Anda memohon suatu proses dengan cara ini Anda dapat dengan bebas menutup proses induk aslinya dan itu akan terus diangkut - itu secara efektif tidak diakui. Ini juga berarti Anda tidak dapat menggunakan konvensional waitperintah, tetapi menunggu di ps's kembali harus lebih kuat dalam hal apapun.

Layak disebutkan, saya pikir, adalah bahwa proses ini sebenarnya pada awalnya dipanggil $(command substitution)dan printfssaya yang $infosaya inginkan sehingga saya dapat secara efektif mengendalikannya. Tetapi begitu ia menjatuhkan output terminalnya exec 1>&2(yang ditutup dengan subkulit yang sama dengan 2>&-), proses lolos dan saya harus menunggu untuk itu di ujung yang lain. Agak yang terbaik dari kedua dunia, terutama jika Anda menggunakannya untuk menangani pipa input, selama Anda dapat membungkus pikiran Anda di sekitar semua pengalihan dan pemimpin proses.

Yang lainnya hanya untuk demonstrasi di sini. Yang Anda butuhkan untuk menjalankan ini adalah skrip teratas dan:

info="$(($script_path &)2>&- &)"    

CATATAN: Ini hanya mencetak ke terminal persis apa yang ingin saya tunjukkan. Sebagaimana dicatat oleh$PPID,proses ini tidak diakui oleh terminal dan merupakan anak langsung dari$PID 1.

Jika Anda ingin menjalankan keduanya secara bersamaan dan menunggu mereka, Anda bisa menyerahkan pskeduanya dan menunggu.

mikeserv
sumber