2011-07-03 24 views
20

Tôi đã thích sử dụng một kiểu máy phát điện giống như giữa các hàm trong các kịch bản lệnh shell của tôi. Một cái gì đó như thế này:Thông báo bên phải của đường ống dẫn thất bại bên trái?

parse_commands /da/cmd/file | process_commands 

Tuy nhiên, vấn đề cơ bản với mô hình này là nếu parse_command gặp một lỗi, cách duy nhất tôi đã tìm thấy thông báo process_command rằng nó không là bằng cách nói một cách rõ ràng (ví dụ echo "FILE_NOT_FOUND"). Điều này có nghĩa là mọi hoạt động có khả năng gây lỗi trong parse_command sẽ phải được rào chắn.

Không có cách nào process_command có thể phát hiện thấy phía bên trái đã thoát với mã thoát khác không?

+0

Như mọi khi, googling thứ hai tìm thấy http://www.unix.com/302268337-post4.html – Bittrance

+1

http://stackoverflow.com/questions/32698407/kill-next-command-in-pipeline-on- fail-bash/32699218 # 32699218 là một phiên bản hơi rõ ràng hơn của câu hỏi này, được cho là, câu trả lời hay hơn (câu hỏi hỏi cách thông báo bên phải đường ống thất bại, nhưng câu trả lời được chấp nhận chỉ giải quyết cách phát hiện tình hình trong shell cha). –

Trả lời

14

Quá trình đường ống có tiếp tục ngay cả khi quá trình đầu tiên đã kết thúc hay là vấn đề mà bạn không biết rằng quá trình đầu tiên không thành công?

Nếu sau này, bạn có thể xem biến số PIPESTATUS (đây thực sự là mảng BASH). Điều đó sẽ cung cấp cho bạn mã thoát của lệnh đầu tiên:

parse_commands /da/cmd/file | process_commands 
temp=("${PIPESTATUS[@]}") 
if [ ${temp[0]} -ne 0 ] 
then 
    echo 'parse_commands failed' 
elif [ ${temp[1]} -ne 0 ] 
then 
    echo 'parse_commands worked, but process_commands failed' 
fi 

Nếu không, bạn sẽ phải sử dụng đồng xử lý.

+1

Đúng là điều này cung cấp thông tin, nhưng nó không thể được thực hiện bên trong process_command, do đó khả năng đọc bị ảnh hưởng. – Bittrance

0

Nếu bạn có command1 && command2 thì lệnh 2 sẽ chỉ được thực hiện khi lệnh đầu tiên thành công - nếu không, lệnh ngắn mạch boolean sẽ được thực hiện. và sau đó có lệnh thứ hai lấy từ tập tin đó.

Chỉnh sửa: Bằng cách sử dụng khôn ngoan ; bạn có thể dọn dẹp tệp tạm thời, ví dụ:

(command1 && command2) ; rm temporaryfile 
5

Khác (& &) và nhà điều hành, các nhà điều hành đường ống (|) hoạt động bằng cách đẻ trứng cả hai quá trình cùng một lúc, vì vậy quá trình đầu tiên có thể đường ống đầu ra của nó đến quá trình thứ hai mà không cần đệm dữ liệu trung gian. Điều này cho phép xử lý một lượng lớn dữ liệu với ít bộ nhớ hoặc sử dụng đĩa.

Do đó, trạng thái thoát của quá trình đầu tiên sẽ không khả dụng cho quy trình thứ hai cho đến khi hoàn tất.

+0

Không nhất thiết phải đúng. Xem, ví dụ: http://stackoverflow.com/a/32699218/14122 –

+1

@Chuyển đổi tốt tôi vẫn muốn nói rằng bạn chỉ có thể nhận trạng thái thoát của quá trình đầu tiên khi nó đã thoát. Nhưng thực sự theo các câu trả lời khác nhau, kể cả các câu trả lời của bạn, sử dụng các âm thanh FIFO hoặc PIPESTATUS như một giải pháp linh hoạt cho câu hỏi OP cho phép truy cập trạng thái thoát theo cách đầy đủ. – jjmontes

4

Bạn có thể thử một số công việc arround sử dụng một FIFO:

mkfifo /tmp/a 
cat /tmp/a | process_commands & 

parse_cmd /da/cmd/file > /tmp/a || (echo "error"; # kill process_commands) 
+0

Vâng, tôi nghĩ rằng đây là điều tốt nhất bạn có thể làm mà không cần viết một chương trình C mà gần như cùng một điều, chỉ với một đường ống thông thường chứ không phải là một đường ống được đặt tên. Tôi tự hỏi của cơ sở coproc zsh sẽ giúp đỡ với điều này, nhưng tôi không thực sự biết nhiều về nó. – andrewdski

0

Có một cách để làm điều này trong bash 4.0, mà thêm coproc dựng sẵn từ tro. Cơ sở coprocess này được mượn từ ksh, sử dụng một cú pháp khác. Vỏ duy nhất tôi có quyền truy cập vào hệ thống hỗ trợ coprocesses là ksh. Đây là giải pháp được viết bằng ksh:

parse_commands /da/cmd/file |& 
parser=$! 

process_commands <&p & 
processor=$! 

if wait $parser 
then 
    wait $processor 
    exit $? 
else 
    kill $processor 
    exit 1 
fi 

Ý tưởng là bắt đầu parse_commands ở chế độ nền với đường ống kết nối với vỏ chính. Pid được lưu trong parser. Sau đó, process_commands được bắt đầu với đầu ra của parse_commands làm đầu vào của nó. (Đó là những gì <&p làm.) Điều này cũng được đặt trong nền với pid của nó được lưu trong processor.

Với cả hai người trong nền được kết nối bằng đường ống, vỏ chính của chúng tôi được tự do chờ trình phân tích cú pháp chấm dứt. Nếu nó chấm dứt mà không có lỗi, chúng tôi chờ bộ xử lý kết thúc và thoát với mã trả về của nó. Nếu nó chấm dứt với lỗi, chúng tôi sẽ xóa bộ xử lý và thoát với trạng thái khác 0. Nó cần được khá đơn giản để dịch này để sử dụng bash 4.0/tro coproc nội trang, nhưng tôi không có tài liệu tốt, cũng không phải là một cách để kiểm tra điều đó.

+0

Làm thế nào để bạn đảm bảo rằng 'process_commands' không thoát khỏi việc nhấn EOF (khi kết thúc ghi của FIFO được đóng) trước khi nhận được tín hiệu? –

0

Bạn có thể chạy parse_commands /da/cmd/file trong một subshell rõ ràng và echo trạng thái thoát của subshell này thông qua các đường ống để process_commands cũng được chạy trong một subshell rõ ràng để xử lý dữ liệu đường ống chứa trong /dev/stdin.

Còn lâu mới là tao nhã, nhưng dường như để hoàn thành công việc :)

Một ví dụ đơn giản:

(
(ls -l ~/.bashrcxyz; echo $?) | 
( 
piped="$(</dev/stdin)"; 
[[ "$(tail -n 1 <<<"$piped")" -eq 0 ]] && printf '%s\n' "$piped" | sed '$d' || exit 77 
); 
echo $? 
) 
0

gì về:

parse_commands /da/cmd/file > >(process_commands) 
+0

Không có ở đây. Thử nghiệm với 'false>> (/ bin/echo foo)' – xebeche

28

Sử dụng set -o pipefail trên đầu trang của bash của bạn kịch bản để khi bên trái của đường ống bị lỗi (trạng thái thoát! = 0), phía bên phải không thực thi.

+7

"set -o pipefail" không ngăn cản bên phải được thực hiện, do đó, nó không trả lời cho câu hỏi ban đầu là "cách thông báo cho phía bên phải ... "Về cơ bản thay đổi trạng thái thoát của đường ống: theo mặc định, trạng thái thoát của đường ống là trạng thái thoát của phía bên phải, với tùy chọn này, đó là số lượng lệnh tối đa của đường ống. – mcoolive

+3

Nhưng đó là những gì tôi đã tìm kiếm :-) Vì vậy, nó giúp tôi. Thx – mcoolive

+0

sẽ làm cho nó trông giống như toàn bộ lệnh không thành công? – dtracers

2

tôi không có đủ uy tín để bình luận, nhưng accepted answer đã mất tích một bế mạc } trên dòng 5.

Sau khi sửa chữa này, mã sẽ ném một lỗi -ne: unary operator expected, mà điểm đến một vấn đề: PIPESTATUS là ghi đè bởi điều kiện theo lệnh if, vì vậy giá trị trả lại của process_commands sẽ không bao giờ được kiểm tra!

Điều này là do [ ${PIPESTATUS[0]} -ne 0 ]equivalent totest ${PIPESTATUS[0]} -ne 0, thay đổi $PIPESTATUS giống như bất kỳ lệnh nào khác. Ví dụ:

return0() { return 0;} 
return3() { return 3;} 

return0 | return3 
echo "PIPESTATUS: ${PIPESTATUS[@]}" 

Điều này trả về PIPESTATUS: 0 3 như mong đợi. Nhưng nếu chúng ta giới thiệu điều kiện thì sao?

return0 | return3 
if [ ${PIPESTATUS[0]} -ne 0 ]; then 
    echo "1st command error: ${PIPESTATUS[0]}" 
elif [ ${PIPESTATUS[1]} -ne 0 ]; then 
    echo "2nd command error: ${PIPESTATUS[1]}" 
else 
    echo "PIPESTATUS: ${PIPESTATUS[@]}" 
    echo "Both return codes = 0." 
fi 

Chúng tôi nhận được lỗi [: -ne: unary operator expected, và điều này:

PIPESTATUS: 2 
Both return codes = 0. 

Để sửa lỗi này, $PIPESTATUS nên được lưu trữ trong một biến mảng khác nhau, như vậy:

return0 | return3 
TEMP=("${PIPESTATUS[@]}") 
echo "TEMP: ${TEMP[@]}" 
if [ ${TEMP[0]} -ne 0 ]; then 
    echo "1st command error: ${TEMP[0]}" 
elif [ ${TEMP[1]} -ne 0 ]; then 
    echo "2nd command error: ${TEMP[1]}" 
else 
    echo "TEMP: ${TEMP[@]}" 
    echo "All return codes = 0." 
fi 

nào in:

TEMP: 0 3 
2nd command error: 3 

như dự định.

Chỉnh sửa: Tôi đã sửa câu trả lời được chấp nhận, nhưng tôi để lại giải thích này cho hậu thế.

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