bash: làm thế nào để tuyên truyền lỗi trong quá trình thay thế?


19

Tôi muốn các kịch bản shell của tôi thất bại bất cứ khi nào một lệnh được thực thi với chúng thất bại.

Thông thường tôi làm điều đó với:

set -e
set -o pipefail

(thông thường tôi set -ucũng thêm )

Điều này là không có cái nào ở trên hoạt động với sự thay thế quá trình. Mã này in "ok" và thoát với mã trả về = 0, trong khi tôi muốn nó thất bại:

#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)

Có bất cứ điều gì tương đương với "pipefail" nhưng để thay thế quá trình? Bất kỳ cách nào khác để chuyển đến một lệnh đầu ra của các lệnh vì chúng là các tệp, nhưng gây ra lỗi bất cứ khi nào bất kỳ chương trình nào trong số đó không thành công?

Một giải pháp cho người nghèo sẽ được phát hiện nếu những lệnh đó ghi vào stderr (nhưng một số lệnh ghi vào stderr trong các tình huống thành công).

Một giải pháp tương thích posix khác sẽ sử dụng các ống có tên, nhưng tôi cần phải sử dụng các lệnh thay thế quy trình sử dụng đó như các onelin được xây dựng từ mã được biên dịch và tạo các ống có tên sẽ làm phức tạp mọi thứ (lệnh thêm, lỗi bẫy xóa chúng, v.v.)


Bạn không cần phải bẫy lỗi để xóa các đường ống có tên. Trên thực tế, đó không phải là một cách tốt. Hãy xem xét mkfifo pipe; { rm pipe; cat <file; } >pipe. Lệnh đó sẽ bị treo cho đến khi một trình đọc mở ra pipebởi vì nó là trình bao, open()và ngay sau khi có một trình đọc trên pipeliên kết fs piperm'd và sau đó catsao chép vào phần mô tả của trình bao cho đường ống đó. Và dù sao đi nữa, nếu bạn muốn tuyên truyền một lỗi ra khỏi một quy trình phụ, hãy thực hiện: <( ! : || kill -2 "$$")
mikeerv

Cảm ơn đã chín muồi trong việc xóa tên ống. Thật không may, sự $$thay thế không hoạt động đối với tôi, vì việc thay thế lệnh không được thực hiện vì lệnh sử dụng thay thế quy trình được thực hiện bên trong một đường ống lệnh được sinh ra từ mã "không vỏ" (python). Có lẽ tôi nên tạo quy trình con trong python và dẫn chúng theo chương trình.
juanleon

Vì vậy, sử dụng kill -2 0.
mikeerv

Thật không may "giết -2 0" sẽ giết (tín hiệu) trăn. Viết trình xử lý tín hiệu để thực hiện logic nghiệp vụ trong một ứng dụng đa luồng không phải là điều tôi mong đợi :-)
juanleon

Nếu bạn không muốn xử lý tín hiệu thì tại sao bạn lại cố gắng nhận chúng? Dù sao, tôi hy vọng các đường ống được đặt tên sẽ là giải pháp đơn giản nhất cuối cùng, nếu chỉ vì làm đúng yêu cầu đặt ra khuôn khổ của riêng bạn và thiết lập bộ điều phối riêng của bạn. Hãy vượt qua rào cản đó và tất cả cùng xuất hiện.
mikeerv

Câu trả lời:


6

Bạn chỉ có thể giải quyết vấn đề đó với ví dụ:

cat <(false || kill $$) <(echo ok)
other_command

Subshell của script là SIGTERMd trước khi lệnh thứ hai có thể được thực thi ( other_command). Các echo oklệnh được thực thi "thỉnh thoảng": Vấn đề là quá trình thay thế là không đồng bộ. Không có gì bảo đảm rằng các kill $$lệnh được thực thi trước hoặc sau khi các echo oklệnh. Đó là vấn đề lập kế hoạch hệ điều hành.

Hãy xem xét một tập lệnh bash như thế này:

#!/bin/bash
set -e
set -o pipefail
cat <(echo pre) <(false || kill $$) <(echo post)
echo "you will never see this"

Đầu ra của tập lệnh đó có thể là:

$ ./script
Terminated
$ echo $?
143           # it's 128 + 15 (signal number of SIGTERM)

Hoặc là:

$ ./script
Terminated
$ pre
post

$ echo $?
143

Bạn có thể thử nó và sau một vài lần thử, bạn sẽ thấy hai đơn hàng khác nhau trong đầu ra. Trong trường hợp đầu tiên, tập lệnh đã bị chấm dứt trước khi hai echolệnh còn lại có thể ghi vào bộ mô tả tập tin. Trong cái thứ hai false, killlệnh hoặc có thể được lên lịch sau các echolệnh.

Hay nói chính xác hơn: Cuộc gọi hệ thống signal()của sự killvô dụng gửi SIGTERMtín hiệu đến quá trình shell đã được lên lịch (hoặc được gửi) muộn hơn hoặc sớm hơn các tòa nhà vang vọng write().

Tuy nhiên, tập lệnh dừng lại và mã thoát không phải là 0. Do đó, nó sẽ giải quyết vấn đề của bạn.

Tất nhiên, một giải pháp khác là sử dụng các đường ống có tên cho việc này. Nhưng, nó phụ thuộc vào kịch bản của bạn mức độ phức tạp khi thực hiện các đường dẫn có tên hoặc cách giải quyết ở trên.

Tài liệu tham khảo:


2

Đối với hồ sơ, và ngay cả khi câu trả lời và nhận xét là tốt và hữu ích, tôi đã kết thúc việc thực hiện một cái gì đó hơi khác một chút (tôi có một số hạn chế về việc nhận tín hiệu trong quy trình phụ huynh mà tôi không đề cập trong câu hỏi)

Về cơ bản, tôi đã kết thúc việc làm một cái gì đó như thế này:

command <(subcomand 2>error_file && rm error_file) <(....) ...

Sau đó tôi kiểm tra tệp lỗi. Nếu nó tồn tại, tôi biết những gì tiểu ban không thành công (và nội dung của error_file có thể hữu ích). Nhiều dài dòng và hackish mà ban đầu tôi muốn, nhưng ít cồng kềnh hơn so với việc tạo ra các đường ống có tên trong một giao dịch bash một lớp lót.


Bất cứ điều gì làm việc cho bạn thường là cách tốt nhất. Cảm ơn đặc biệt vì đã trở lại và trả lời - ảnh tự chụp là sở thích của tôi.
mikeerv

2

Ví dụ này cho thấy làm thế nào để sử dụng killcùng với trap.

#! /bin/bash
failure ()
{
  echo 'sub process failed' >&2
  exit 1
}
trap failure SIGUSR1
cat < <( false || kill -SIGUSR1 $$ )

Nhưng killkhông thể chuyển mã trả về từ quy trình phụ của bạn sang quy trình cha.


Tôi thích biến thể này trên câu trả lời được chấp nhận
sehe

1

Theo cách tương tự như bạn sẽ triển khai $PIPESTATUS/ $pipestatusvới trình bao POSIX không hỗ trợ nó , bạn có thể có được trạng thái thoát của các lệnh bằng cách chuyển chúng xung quanh qua một đường ống:

unset -v false_status echo_status
{ code=$(
    exec 3>&1 >&4 4>&-
    cat 3>&- <(false 3>&-; echo >&3 "false_status=$?") \
             <(echo ok 3>&-; echo >&3 "echo_status=$?")
);} 4>&1
cat_status=$?
eval "$code"
printf '%s_code=%d\n' cat   "$cat_status" \
                      false "$false_status" \
                      echo  "$echo_status"

Cung cấp cho:

ok
cat_code=0
false_code=1
echo_code=0

Hoặc bạn có thể sử dụng pipefailvà thực hiện thay thế quy trình bằng tay như bạn làm với các trình bao không hỗ trợ nó:

set -o pipefail
{
  false <&5 5<&- | {
    echo OK <&5 5<&- | {
      cat /dev/fd/3 /dev/fd/4
    } 4<&0 <&5 5<&-
  } 3<&0
} 5<&0
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.