Script Bash menunggu proses dan mendapatkan kode kembali

13

Saya mencoba membuat skrip yang akan memulai banyak perintah latar belakang. Untuk setiap perintah latar belakang saya perlu mendapatkan kode kembali.

Saya telah mencoba skrip berikut:

 #!/bin/bash
set -x
pid=()
return=()


for i in 1 2
do
 echo start $i
 ssh mysql "/root/test$i.sh" &
 pid[$i]=$!
done

for i in ${#pid[@]}
do
echo ${pid[$i]}
wait ${pid[$i]}
return[$i]=$?

if [ ${return[$i]} -ne 0 ]
then
  echo mail error
fi

done

echo ${return[1]}
echo ${return[2]}

Masalah saya adalah selama loop menunggu, jika pid kedua selesai sebelum yang pertama, saya tidak akan bisa mendapatkan kode kembali.

Saya tahu bahwa saya bisa menjalankan menunggu pid1 pid2, tetapi dengan perintah ini saya tidak bisa mendapatkan kode kembali dari semua perintah.

Ada ide ?

Hugo
sumber

Jawaban:

6

Anda dapat melakukan ini dengan menggunakan direktori sementara.

# Create a temporary directory to store the statuses
dir=$(mktemp -d)

# Execute the backgrouded code. Create a file that contains the exit status.
# The filename is the PID of this group's subshell.
for i in 1 2; do
    { ssh mysql "/root/test$i.sh" ; echo "$?" > "$dir/$BASHPID" ; } &
done

# Wait for all jobs to complete
wait

# Get return information for each pid
for file in "$dir"/*; do
    printf 'PID %d returned %d\n' "${file##*/}" "$(<"$file")"
done

# Remove the temporary directory
rm -r "$dir"
Chris Down
sumber
9

Masalahnya lebih pada Anda

for i in ${#pid[@]}

Yang mana for i in 2.

Seharusnya lebih dari:

for i in 1 2

atau

for ((i = 1; i <= ${#pid[@]}; i++))

wait "$pid" akan mengembalikan kode keluar dari pekerjaan dengan bash(dan POSIX shells, tetapi tidak zsh) bahkan jika pekerjaan sudah dihentikan ketika waitdimulai.

Stéphane Chazelas
sumber
5

Implementasi umum tanpa file sementara.

#!/usr/bin/env bash

## associative array for job status
declare -A JOBS

## run command in the background
background() {
  eval $1 & JOBS[$!]="$1"
}

## check exit status of each job
## preserve exit status in ${JOBS}
## returns 1 if any job failed
reap() {
  local cmd
  local status=0
  for pid in ${!JOBS[@]}; do
    cmd=${JOBS[${pid}]}
    wait ${pid} ; JOBS[${pid}]=$?
    if [[ ${JOBS[${pid}]} -ne 0 ]]; then
      status=${JOBS[${pid}]}
      echo -e "[${pid}] Exited with status: ${status}\n${cmd}"
    fi
  done
  return ${status}
}

background 'sleep 1 ; false'
background 'sleep 3 ; true'
background 'sleep 2 ; exit 5'
background 'sleep 5 ; true'

reap || echo "Ooops! Some jobs failed"
Jon Nalley
sumber
Terima kasih :-) Ini persis apa yang saya cari!
Qorbani
0

Jawaban Stéphane bagus, tapi saya lebih suka

for i in ${!pid[@]}
do
    wait ${pid[i]}
    return[i]=$?
    unset "pid[$i]"
done

yang akan mengulangi kunci pidarray, terlepas dari entri mana yang masih ada, sehingga Anda dapat mengadaptasinya, keluar dari loop, dan memulai kembali seluruh loop dan itu hanya akan bekerja. Dan Anda tidak perlu nilai berturut-turut iuntuk memulai.

Tentu saja, jika Anda berurusan dengan ribuan proses maka mungkin pendekatan Stépane akan lebih efisien jika Anda memiliki daftar yang tidak jarang.

Martin Kealey
sumber