Bash, làm thế nào để cho một số quá trình nền chạy nhưng chờ người khác?


11

Tôi có (chưa) khác wait, &, &&câu hỏi kiểm soát dòng chảy ..

Nói rằng tôi có một kịch bản giống như thế này, nơi tôi muốn thực hiện càng nhiều công việc cùng một lúc càng tốt:

# 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

Câu hỏi 1: Trong kịch bản, có combinephải đợi cả hai somethingElsequá trình kết thúc trong khi cả hai somethingquá trình tiếp tục không?

Câu hỏi 2: Nếu không - và tôi nghi ngờ là không - làm thế nào để tôi combinechỉ chờ đợi cho cả hai somethingElsequy trình trong khi somethingcác quy trình trên tiếp tục hoạt động ở chế độ nền?

Câu trả lời:


13

Trong ví dụ của bạn, combinelệnh sẽ chỉ được chạy ngay khi thoát khỏi khung con (và cung cấp quá trình nền cuối cùng được bắt đầu mà không có lỗi). Subshell sẽ thoát ngay lập tức sau khi các công việc được bắt đầu vì không có waitlệnh.

Nếu bạn muốn thực thi một lệnh dựa trên giá trị trả về của hai hoặc nhiều quá trình nền đồng thời, thì tôi không thể thấy bất kỳ cách nào khác ngoài việc sử dụng các tệp tạm thời cho các giá trị trả về. Điều này là do waitchỉ có thể trả về giá trị trả về của một trong các quy trình mà nó chờ. Ngoài ra, vì các quy trình nền phải được chạy trong các lớp con để có được giá trị trả về của chúng, chúng không thể được lưu trữ trong các biến. Bạn có thể làm:

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"

Nếu bạn không thực sự quan tâm đến các giá trị trả về, bạn có thể bắt đầu công việc một cách bình thường và sử dụng wait:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

somethingElse InputA >OutputA &
proc1=$!

somethingElse InputB >OutputB &
proc2=$!

wait "$proc1" "$proc2"
combine OutputA OutputB >Result

Xin chào, tôi nghĩ rằng tùy chọn thứ 2 sẽ phù hợp với tôi ...
Stephen Henderson

3

Quá trình thay thế sẽ hiệu quả hơn, đặc biệt là nếu bạn không cần lưu các tệp OutputAOutputBchỉ quan tâm đến Result? Điều này có đặc biệt tiết kiệm thời gian không vì nếu bạn có I / O chậm khi ghi vào đĩa, lưu các tệp OutputAOutputBcó thể là bước giới hạn tốc độ?

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

Quá trình thay thế cho phép bạn đặt lệnh bên trong <(..here..)thay vì lưu đầu ra vào một tệp, sau đó đọc từ đó làm đầu vào trong bước "kết hợp".

Nếu bộ nhớ là một giới hạn, và kích thước của outputAoutputBnhiều hơn những gì bộ nhớ có thể giữ, nó sẽ đánh bại toàn bộ mục đích?

Sẽ combineđợi cho đến khi cả hai quá trình được hoàn thành trước khi nó bắt đầu chạy?


Đây không phải là Jeopardy mệnh; xin vui lòng không diễn đạt câu trả lời của bạn dưới dạng câu hỏi. Nghiêm túc mà nói, bạn đã nghĩ ra một ý tưởng mới và tôi nghĩ nó khá hay. Để trả lời một vài điểm của bạn: combinesẽ bắt đầu chạy ngay khi hai somethingElselệnh bắt đầu, nhưng không sao, vì <(…)mọi thứ đều là đường ống; vì vậy combineđơn giản là sẽ bị buộc phải chờ dữ liệu nếu nó vượt somethingElsequá các quy trình. Và, bởi vì chúng là đường ống, kích thước không phải là vấn đề. Tiết (Cont'd)
G-Man nói 'Tái lập lại Monica'

(Tiếp theo) Vấn đề thực sự duy nhất tôi có với câu trả lời của bạn là nó không cho phép kiểm tra trạng thái thoát của các somethingElsequy trình - và nó không hoàn toàn rõ ràng liệu điều đó có quan trọng với người hỏi hay không. Nhưng, cũng, một câu trả lời không nên đặt câu hỏi như vậy.
G-Man nói 'Phục hồi Monica'

2

Bạn có thể sử dụng waitlệnh:

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

Bạn có thể thấy dòng "bắt đầu" xảy ra ngay lập tức và "xong" chờ trong 10 giây.


nói chung chờ đợi yêu cầu các quá trình con của cùng một vỏ. Chờ đã khá khó khăn đấy.
mikeerv

1
@mikeerv, bạn đang nói về cái gì vậy? Đó là vấn đề: nó chờ đợi tất cả những đứa trẻ trong đó.
psusi

bằng các thử nghiệm ban đầu của tôi, nó hoạt động. Bây giờ tôi sẽ thử nó trên kịch bản lớn
Stephen Henderson

Chính xác - con cùng vỏ - vỏ phụ . Nó nên hoạt động cho bất kỳ quá trình nào không cố gắng thoát - hoặc daemonize hoặc bất cứ điều gì. Đó là tất cả những gì tôi muốn nói - miễn là các quy trình của bạn tôn trọng các nhà lãnh đạo quy trình chờ đợi, nhưng ngay khi một quy trình cố gắng trở thành nhà lãnh đạo quy trình của riêng mình, chờ đợi sẽ có vấn đề.
mikeerv

0

Tôi thực sự chứng minh chính xác làm thế nào loại điều này có thể được thực hiện trong một câu trả lời khác ở đây . Câu trả lời đó là một câu hỏi về việc đảm bảo 2 bản ghi được duy trì bởi một quy trình nền, vì vậy tôi đã chứng minh nó bằng 10.

Tập lệnh demo

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

Chạy thử

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

Đầu ra:

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

Những điều trên chứng tỏ. Nó được xây dựng và chạy một kịch bản được đặt tên /tmp/script, chmod nó như là thực thi, và chạy nó trong &backgroundmột &backgrounded ( subshell ).

Tập lệnh rms /tmp/file0-910 tập tin và echoesmột dòng mỗi giây vào tất cả 10 tập tin. Tôi chụp một số $infotừ quá trình bị từ chối và trình bày nó qua $(command substitution). While pscác báo cáo vẫn còn trên $pidtôi chụp, tôi biết nó vẫn chạy nên sleep.khi tôi hoàn thành, các dòng trong tất cả 10 tệp được tính vớiwc.

Sau khi bạn gọi một quy trình theo cách này, bạn có thể tự do đóng quy trình cha mẹ ban đầu của nó và nó sẽ tiếp tục vận chuyển - nó thực sự bị từ chối. Điều này cũng có nghĩa là bạn không thể sử dụng waitlệnh thông thường , nhưng chờ đợi pssự trở lại sẽ mạnh mẽ hơn trong mọi trường hợp.

Đáng nói, tôi nghĩ, đó là quá trình thực sự được gọi ban đầu $(command substitution)printfstôi $infomuốn tôi có thể kiểm soát nó một cách hiệu quả. Nhưng ngay sau khi nó giảm đầu ra đầu cuối của nó với exec 1>&2(được đóng trong cùng một mạng con với 2>&-), quá trình sẽ thoát và tôi phải đợi nó ở đầu bên kia. Loại tốt nhất của cả hai thế giới, đặc biệt là nếu bạn sử dụng nó để xử lý các đường ống đầu vào, miễn là bạn có thể bao bọc tâm trí của mình xung quanh tất cả các chuyển hướng và các nhà lãnh đạo quá trình.

Mọi thứ khác chỉ là để trình diễn ở đây. Tất cả những gì bạn cần để chạy đây là tập lệnh hàng đầu và:

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

LƯU Ý: Điều này chỉ in ra thiết bị đầu cuối chính xác những gì tôi muốn thể hiện nó. Theo ghi nhận của$PPID,quá trình này, thiết bị đầu cuối bị từ chối và là con trực tiếp của$PID 1.

Nếu bạn muốn chạy hai trong số này đồng thời và chờ đợi chúng, bạn có thể chỉ cần đưa pscả hai nắp của chúng và chờ đợi.

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.