สคริปต์ Bash รอกระบวนการและรับรหัสส่งคืน


13

ฉันพยายามสร้างสคริปต์ซึ่งจะเริ่มคำสั่งพื้นหลังจำนวนมาก สำหรับแต่ละคำสั่งพื้นหลังฉันต้องได้รับรหัสส่งคืน

ฉันลองใช้สคริปต์ต่อไปนี้แล้ว:

 #!/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]}

ปัญหาของฉันอยู่ระหว่างการรอลูปถ้า pid ที่สองเสร็จสิ้นก่อนอันแรกฉันจะไม่สามารถรับรหัสส่งคืนได้

ฉันรู้ว่าฉันสามารถเรียกใช้รอ pid1 pid2 แต่ด้วยคำสั่งนี้ฉันไม่สามารถรับรหัสส่งคืนของคำสั่งทั้งหมดได้

ความคิดใด ๆ

คำตอบ:


6

คุณสามารถทำได้โดยใช้ไดเรกทอรีชั่วคราว

# 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"

9

ปัญหานี้เกิดขึ้นกับคุณมากขึ้น

for i in ${#pid[@]}

for i in 2ซึ่งเป็น

มันควรจะเป็น:

for i in 1 2

หรือ

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

wait "$pid" จะส่งคืนรหัสทางออกของงานด้วยbash(และเชลล์ POSIX แต่ไม่ใช่zsh) แม้ว่างานนั้นจะยุติลงเมื่อwaitเริ่มทำงาน


5

การใช้งานทั่วไปที่ไม่มีไฟล์ชั่วคราว

#!/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"

ขอบคุณ :-) นี่คือสิ่งที่ฉันกำลังมองหา!
Qorbani

0

คำตอบของStéphaneนั้นดี แต่ฉันต้องการ

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

ซึ่งจะวนซ้ำคีย์ของpidอาร์เรย์โดยไม่คำนึงว่ามีรายการใดอยู่ดังนั้นคุณสามารถปรับเปลี่ยนแยกลูปและเริ่มลูปทั้งหมดใหม่อีกครั้งและมันจะทำงานได้ และคุณไม่จำเป็นต้องมีค่าiเริ่มต้นติดต่อกัน

แน่นอนถ้าคุณกำลังรับมือกับกระบวนการหลายพันกระบวนการบางทีStépaneอาจมีประสิทธิภาพมากกว่าเล็กน้อยเมื่อคุณมีรายการที่ไม่กระจัดกระจาย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.