2015-09-21 17 views
6

Tôi có một kịch bản trực tuyến sao lưu mà tôi đang chạy như sau:Kill lệnh tiếp theo trong đường ống trên thất bại (Bash)

./backup_script.sh | aws s3 cp - s3://bucket/path/to/backup 

Các aws lệnh suối stdin để lưu trữ đám mây một cách nguyên tử. Nếu quá trình bị gián đoạn mà không có EOF, quá trình tải lên sẽ bị hủy.

Tôi muốn quy trình aws bị hủy nếu ./backup_script.sh thoát với mã thoát khác 0.

Bất kỳ mẹo bash nào để thực hiện việc này?

EDIT: Bạn có thể kiểm tra giải pháp của bạn với kịch bản này:

#!/usr/bin/env python 
import signal 
import sys 
import functools 

def signal_handler(signame, signum, frame): 
    print "Got {}".format(signame) 
    sys.exit(0) 

signal.signal(signal.SIGTERM, functools.partial(signal_handler, 'TERM')) 
signal.signal(signal.SIGINT, functools.partial(signal_handler, 'INT')) 

for i in sys.stdin: 
    pass 

print "Got EOF" 

Ví dụ:

$ grep --bla | ./sigoreof.py 
grep: unrecognized option `--bla' 
usage: grep [-abcDEFGHhIiJLlmnOoqRSsUVvwxZ] [-A num] [-B num] [-C[num]] 
    [-e pattern] [-f file] [--binary-files=value] [--color=when] 
    [--context[=num]] [--directories=action] [--label] [--line-buffered] 
    [--null] [pattern] [file ...] 
Got EOF 

Tôi muốn ./sigoreof.py để được chấm dứt với một tín hiệu.

+1

Tôi giả định rằng vấn đề của bạn là một sự thất bại của backup_script.sh chỉ đóng các đường ống như xa như quá trình nhận 'aws' là có liên quan; aws không thể phát hiện lỗi và giả định bản sao lưu đã ổn. –

+0

Bên cạnh: 'sigoreof' không thực sự báo cáo tín hiệu cho tôi, mặc dù nó chắc chắn có thể phân biệt được với trường hợp EOF. './ftest: dòng 11: 53793 Chấm dứt: 15 ./sigoreof < aws.fifo 3> & -' –

+0

Như được chỉ ra bởi @tourism: Có khả năng dupe của http://stackoverflow.com/questions/6565694/left-side-failure-on- pipe-in-bash (nhưng câu trả lời không thực sự giải quyết câu hỏi được hỏi). –

Trả lời

1

Một kịch bản ngắn trong đó sử dụng thay thế tiến trình thay vì đặt tên đường ống sẽ là:

#!/bin/bash 

exec 4> >(./second-process.sh) 
./first-process.sh >&4 & 
if ! wait $! ; then echo "error in first process" >&2; kill 0; wait; fi 

Nó hoạt động giống như với một fifo, về cơ bản sử dụng fd là vật mang tin cho IPC thay vì một tập tin Tên.

Hai nhận xét: Tôi không chắc chắn liệu cần phải đóng fd 4 hay không; Tôi sẽ giả định rằng khi script thoát khỏi shell, đóng tất cả các file đang mở.

Và tôi không thể tìm ra cách để có được PID của quá trình trong quá trình thay thế (ai? Ít nhất trên Cygwin của tôi bình thường $! không hoạt động.) Vì vậy, tôi phải giết tất cả các quy trình trong nhóm , có thể không được mong muốn (nhưng tôi không hoàn toàn chắc chắn về ngữ nghĩa).

1

Tôi nghĩ rằng bạn cần phải sinh ra cả hai quy trình từ một thứ ba và sử dụng cách tiếp cận đường ống có tên từ Lynch trong bài được đề cập bởi @tourism (bên dưới các câu trả lời); hoặc giữ đường ống trực tiếp nhưng viết lại backup_script.sh sao cho nó vẫn còn sống trong trường hợp lỗi, giữ stdout mở. backup_script.sh sẽ phải báo hiệu tình trạng lỗi cho quá trình gọi (ví dụ bằng cách gửi SIGUSR đến ID tiến trình cha), lần lượt đầu tiên giết chết quá trình aws (dẫn đến hủy bỏ nguyên tử) và chỉ sau đó backup_script.sh, trừ khi nó đã thoát vì ống bị hỏng.

+0

'backup_script.sh' có thể là bất kỳ thứ gì từ tập lệnh tùy chỉnh đến' mysqldump'. Tôi cần một cái gì đó chung chung để viết lại 'backup_script.sh' không phải là một lựa chọn – omribahumi

+1

Mọi vấn đề có thể được giải quyết bằng một mức bổ sung của indirection ... bất cứ backup_script_sh nào, bạn luôn có thể bọc nó. –

+0

Đủ công bằng. Có thể nhìn vào đó như là một thay thế cho 'coproc'. Cảm ơn – omribahumi

4

backup_script.shnên có một trạng thái thoát khác không nếu có một lỗi, vì vậy bạn nên kịch bản giống như thế:

if ./backup_script.sh > output.txt; then 
    aws s3 cp output.txt s3://bucket/path/to/backup 
fi 
rm -f output.txt 

Một ống là không thực sự thích hợp ở đây.


Nếu bạn thực sự cần tiết kiệm dung lượng đĩa cục bộ, bạn sẽ phải "đảo ngược" nội dung tải lên; xóa tệp đã tải lên trong trường hợp xảy ra lỗi trong backup_script.sh hoặc tải lên vị trí tạm thời, sau đó di chuyển đến đường dẫn cuối cùng khi bạn đã xác định rằng bản sao lưu đã thành công.

(Để đơn giản, tôi bỏ qua thực tế là bằng cách tự mình thoát khỏi aws thoát trong trường hợp xảy ra lỗi, bạn có thể tải lên nhiều phần sao lưu hơn mức bạn cần. Xem Charles Duffy's answer để có thêm băng thông- cách tiếp cận hiệu quả.)

sau khi bắt đầu quá trình sao lưu với

mkfifo data 
./backup_script.sh > data & writer_pid=$! 

sử dụng một trong các cách sau để tải lên các dữ liệu.

# Upload and remove if there was an error 
aws s3 cp - s3://bucket/path/to/backup < data & 

if ! wait $writer_pid; then 
    aws s3 rm s3://bucket/path/to/backup 
fi 

hoặc

# Upload to a temporary file and move it into place 
# once you know the backup succeeded. 
aws s3 cp - s3://bucket/path/to/backup.tmp < data & 

if wait $writer_pid; then 
    aws s3 mv s3://bucket/path/to/backup.tmp s3://bucket/path/to/backup 
else 
    aws s3 rm s3://bucket/path/to/backup 
fi 
+0

Tôi không muốn giữ 'output.txt' trên đĩa vì lý do không gian. Rõ ràng điều này là có thể với 'coproc'. Tôi đang xem xét nó ngay bây giờ. – omribahumi

+0

Tôi giả định rằng bạn có nhiều không gian đĩa hơn bộ nhớ và 'aws' phải đệm dữ liệu vào * ở đâu đó *. – chepner

+0

Nó sử dụng tải lên chunked tải lên 5MB bộ phận – omribahumi

3

Thông qua/sửa một giải pháp ban đầu được đưa ra bởi @Dummy00001:

mkfifo aws.fifo 
exec 3<>aws.fifo # open the FIFO read/write *in the shell itself* 
aws s3 cp - s3://bucket/path/to/backup <aws.fifo 3>&- & aws_pid=$! 
rm aws.fifo # everyone who needs a handle already has one; can remove the directory entry 

if ./backup_script.sh >&3 3>&-; then 
    exec 3>&-  # success: close the FIFO and let AWS exit successfully 
    wait "$aws_pid" 
else 
    kill "$aws_pid" # send a SIGTERM... 
    wait "$aws_pid" # wait for the process to die... 
    exec 3>&-  # only close the write end *after* the process is dead 
fi 

Những điểm quan trọng:

  • Shell mở R/w FIFO để tránh bị chặn (mở để viết chỉ sẽ chặn cho người đọc; điều này cũng có thể tránh được bằng cách gọi người đọc [có nghĩa là, lệnh s3] trong nền trước khi exec mở phần ghi).
  • Phần cuối của FIFO được giữ bởi chính kịch bản lệnh, vì vậy đầu đọc không bao giờ chạm vào cuối tập tin cho đến sau khi tập lệnh cố tình đóng nó.
  • Lệnh xử lý lệnh aws trên đầu ghi của FIFO được đóng một cách rõ ràng (3<&-), vì vậy nó không tự mở (trong trường hợp này exec 3>&- được thực hiện trong phụ huynh sẽ không cho phép nó hoàn thành đọc và thoát) .
+0

Tôi rút lại nhận xét đó; Tôi hơi mờ khi xử lý các bộ mô tả tập tin như thế này, nhưng tôi nghĩ bạn đúng. – chepner

+0

Điều này phức tạp hơn một chút so với câu trả lời của tôi, nhưng nó chắc chắn tiết kiệm được băng thông bằng cách giết chết việc tải lên sớm hơn. – chepner

+0

@chepner, gọi lại tốt: phức tạp - thực sự có một số phòng để đơn giản hóa. –

0

Tôi có tình huống tương tự: tập lệnh shell chứa đường dẫn sử dụng một trong các chức năng riêng của nó và chức năng đó muốn có thể có hiệu lực chấm dứt. Một ví dụ đơn giản mà giả tạo tìm và hiển thị một tập tin:

#!/bin/sh 
a() { find . -maxdepth 1 -name "$1" -print -quit | grep . || exit 101; } 
a "$1" | cat 
echo done 

Ở đây, hàm a cần có khả năng để thực hiện chấm dứt mà nó cố gắng làm bằng cách gọi exit. Tuy nhiên, khi được gọi thông qua một đường ống (dòng 3), nó chỉ chấm dứt quá trình (subshell) của chính nó. Trong ví dụ, thông báo done vẫn xuất hiện.

Một cách để làm việc xung quanh này là để phát hiện khi trong một subshell và gửi một tín hiệu cho phụ huynh:

#!/bin/sh 
die() { [[ $$ == $(exec sh -c 'echo $PPID') ]] && exit $1 || kill $$; } 
a() { find . -maxdepth 1 -name "$1" -print -quit | grep . || die 101; } 
a "$1" | cat 
echo done 

Khi ở trong một subshell các $$ là pid của phụ huynh và các cấu trúc $(exec sh -c 'echo $PPID') là một cách bất khả tri để thu được pid của tiến trình con. Nếu sử dụng bash thì điều này có thể được thay thế bằng $BASHPID.

Nếu subprocess pid$$ khác nhau sau đó nó sẽ gửi một tín hiệu SIGTERM cho phụ huynh (kill $$) thay vì gọi exit.

Trạng thái thoát đã cho (101) không được tuyên truyền bởi kill để tập lệnh thoát với trạng thái 143 (là 128 + 15 trong đó 15 là id của SIGTERM).

Các vấn đề liên quan