Đưa ra hai lệnh nền, chấm dứt lệnh còn lại khi thoát


14

Tôi có một tập lệnh bash đơn giản khởi động hai máy chủ:

#!/bin/bash
(cd ./frontend && gulp serve) & (cd ./backend && gulp serve --verbose)

Nếu lệnh thứ hai thoát, có vẻ như lệnh đầu tiên tiếp tục chạy.

Làm thế nào tôi có thể thay đổi điều này để nếu một trong hai lệnh thoát, lệnh kia bị chấm dứt?

Lưu ý rằng chúng ta không cần kiểm tra mức độ lỗi của các quá trình nền, chỉ là liệu chúng đã thoát.


Tại sao không gulp ./fronend/serve && gulp ./backend/serve --verbose?
heemayl

servelà một đối số, không phải là một tập tin, vì vậy thư mục hiện tại cần phải được đặt.
blah238

1
Ngoài ra đây là các quy trình chạy dài cần chạy đồng thời, xin lỗi nếu điều đó không rõ ràng.
blah238

Câu trả lời:


21

Điều này bắt đầu cả hai quá trình, chờ đợi quá trình đầu tiên kết thúc và sau đó giết chết quá trình khác:

#!/bin/bash
{ cd ./frontend && gulp serve; } &
{ cd ./backend && gulp serve --verbose; } &
wait -n
pkill -P $$

Làm thế nào nó hoạt động

  1. Khởi đầu:

    { cd ./frontend && gulp serve; } &
    { cd ./backend && gulp serve --verbose; } &

    Hai lệnh trên bắt đầu cả hai quá trình trong nền.

  2. Chờ đợi

    wait -n

    Điều này chờ cho một trong hai công việc nền kết thúc.

    Do -ntùy chọn, điều này đòi hỏi bash 4.3 hoặc tốt hơn.

  3. Giết chết

    pkill -P $$

    Điều này giết chết mọi công việc mà quy trình hiện tại là cha mẹ. Nói cách khác, điều này giết chết mọi quá trình nền vẫn đang chạy.

    Nếu hệ thống của bạn không có pkill, hãy thử thay thế dòng này bằng:

    kill 0

    mà cũng giết chết nhóm quy trình hiện tại .

Ví dụ dễ kiểm tra

Bằng cách thay đổi tập lệnh, chúng tôi có thể kiểm tra tập lệnh ngay cả khi không gulpcài đặt:

$ cat script.sh 
#!/bin/bash
{ sleep $1; echo one;  } &
{ sleep $2; echo two;  } &
wait -n
pkill -P $$
echo done

Kịch bản trên có thể được chạy khi bash script.sh 1 3quá trình đầu tiên kết thúc trước. Ngoài ra, người ta có thể chạy nó bash script.sh 3 1và quá trình thứ hai sẽ chấm dứt trước. Trong cả hai trường hợp, người ta có thể thấy rằng điều này hoạt động như mong muốn.


Điều này có vẻ tuyệt vời. Thật không may, các lệnh đó không hoạt động trên môi trường bash của tôi (msysgit). Đó là xấu của tôi vì đã không chỉ định điều đó. Tôi sẽ thử nó trên một hộp Linux thực sự.
blah238

(1) Không phải tất cả các phiên bản bashhỗ trợ -ntùy chọn cho waitlệnh. (2) Tôi đồng ý 100% với câu đầu tiên - giải pháp của bạn bắt đầu hai quy trình, đợi câu đầu tiên kết thúc và sau đó giết chết câu kia. Nhưng câu hỏi cho biết, lỗi , nếu bị lỗi , thì lệnh kia bị chấm dứt? Tôi tin rằng giải pháp của bạn không phải là điều OP muốn. (3) Tại sao bạn đổi (…) &thành { …; } &? Các &lực lượng trong danh sách (lệnh nhóm) để chạy trong một subshell anyway. IMHO, bạn đã thêm các ký tự và có thể giới thiệu sự nhầm lẫn (tôi phải xem xét nó hai lần để hiểu nó) không có lợi ích.
G-Man nói 'Phục hồi Monica'

1
John là chính xác, họ là các máy chủ web nên thường chạy cả hai trừ khi xảy ra lỗi hoặc chúng được báo hiệu chấm dứt. Vì vậy, tôi không nghĩ rằng chúng ta cần kiểm tra mức độ lỗi của từng quy trình, cho dù nó vẫn đang chạy.
blah238

2
pkillkhông có sẵn cho tôi nhưng kill 0dường như có tác dụng tương tự. Ngoài ra tôi đã cập nhật môi trường Git cho Windows của mình và có vẻ như nó wait -nhoạt động ngay bây giờ, vì vậy tôi chấp nhận câu trả lời này.
blah238

1
Hấp dẫn. Mặc dù tôi thấy rằng hành vi đó là tài liệu cho một số phiên bản của kill. các tài liệu trên hệ thống của tôi không đề cập đến nó. Tuy nhiên, kill 0dù sao đi nữa. Tìm tốt
John1024

1

Đây là khó khăn. Đây là những gì tôi nghĩ ra; có thể đơn giản hóa / hợp lý hóa nó:

#!/bin/sh

pid1file=$(mktemp)
pid2file=$(mktemp)
stat1file=$(mktemp)
stat2file=$(mktemp)

while true; do sleep 42; done &
main_sleeper=$!

(cd frontend && gulp serve           & echo "$!" > "$pid1file";
    wait "$!" 2> /dev/null; echo "$?" > "$stat1file"; kill "$main_sleeper" 2> /dev/null) &
(cd backend  && gulp serve --verbose & echo "$!" > "$pid2file";
    wait "$!" 2> /dev/null; echo "$?" > "$stat2file"; kill "$main_sleeper" 2> /dev/null) &
sleep 1
wait "$main_sleeper" 2> /dev/null

if stat1=$(<"$stat1file")  &&  [ "$stat1" != "" ]  &&  [ "$stat1" != 0 ]
then
        echo "First process failed ..."
        if pid2=$(<"$pid2file")  &&  [ "$pid2" != "" ]
        then
                echo "... killing second process."
                kill "$pid2" 2> /dev/null
        fi
fi
if [ "$stat1" = "" ]  &&  \
   stat2=$(<"$stat2file")  &&  [ "$stat2" != "" ]  &&  [ "$stat2" != 0 ]
then
        echo "Second process failed ..."
        if pid1=$(<"$pid1file")  &&  [ "$pid1" != "" ]
        then
                echo "... killing first process."
                kill "$pid1" 2> /dev/null
        fi
fi

wait
if stat1=$(<"$stat1file")
then
        echo "Process 1 terminated with status $stat1."
else
        echo "Problem getting status of process 1."
fi
if stat2=$(<"$stat2file")
then
        echo "Process 2 terminated with status $stat2."
else
        echo "Problem getting status of process 2."
fi
  • Đầu tiên, bắt đầu một quá trình ( while true; do sleep 42; done &) ngủ / tạm dừng mãi mãi. Nếu bạn chắc chắn rằng hai lệnh của bạn sẽ chấm dứt trong một khoảng thời gian nhất định (ví dụ: một giờ), bạn có thể thay đổi điều này thành một giấc ngủ sẽ vượt quá điều đó (ví dụ:sleep 3600 :). Sau đó, bạn có thể thay đổi logic sau để sử dụng điều này như là một thời gian chờ; tức là, giết các tiến trình nếu chúng vẫn chạy sau thời gian đó. (Lưu ý rằng tập lệnh trên hiện không làm điều đó.)
  • Bắt đầu hai quá trình không đồng bộ (nền đồng thời).
    • Bạn không cần ./cho cd.
    • command & echo "$!" > somewhere; wait "$!" là một cấu trúc phức tạp để bắt đầu một quá trình không đồng bộ, nắm bắt PID của nó và sau đó chờ đợi nó; làm cho nó sắp xếp một quá trình tiền cảnh (đồng bộ). Nhưng điều này xảy ra trong toàn bộ (…)danh sách ở chế độ nền, vì vậy các gulpquy trình sẽ chạy không đồng bộ.
    • Sau khi một trong hai gulptiến trình thoát ra, hãy ghi trạng thái của nó vào một tệp tạm thời và giết quá trình Ngủ mãi mãi.
  • sleep 1 để bảo vệ chống lại tình trạng chủng tộc trong đó quá trình nền đầu tiên bị chết trước khi lần thứ hai có cơ hội ghi PID của nó vào tệp.
  • Đợi cho quá trình ngủ mãi mãi quá trình chấm dứt. Điều này xảy ra sau khi một trong các gulpquá trình thoát ra, như đã nêu ở trên.
  • Xem quá trình nền kết thúc. Nếu thất bại, giết người khác.
  • Nếu một quá trình thất bại và chúng ta đã giết chết tiến trình kia, hãy đợi quá trình thứ hai kết thúc và lưu trạng thái của nó vào một tệp. Nếu quá trình đầu tiên kết thúc thành công, hãy đợi quá trình thứ hai kết thúc.
  • Kiểm tra trạng thái của hai quá trình.

1

Để hoàn thiện ở đây là những gì tôi đã kết thúc bằng cách sử dụng:

#!/bin/bash
(cd frontend && gulp serve) &
(cd backend && gulp serve --verbose) &
wait -n
kill 0

Điều này hoạt động với tôi trên Git cho Windows 2.5.3 64-bit. Các phiên bản cũ hơn có thể không chấp nhận -ntùy chọn trên wait.


1

Trên hệ thống của tôi (Centos), waitkhông có -nnên tôi đã làm điều này:

{ sleep 3; echo one;  } &
FOO=$!
{ sleep 6; echo two;  } &
wait $FOO
pkill -P $$

Điều này không chờ đợi "hoặc", thay vì chờ đợi đầu tiên. Nhưng nó vẫn có thể giúp nếu bạn biết máy chủ nào sẽ bị dừng trước.


Việc wait-ntùy chọn hay không phụ thuộc vào trình bao bạn đang sử dụng, không phải bản phân phối Linux.
Kusalananda
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.