Lưu ý: zsh
sẽ phàn nàn về "mẫu xấu" nếu bạn không định cấu hình nó để chấp nhận "nhận xét nội tuyến" cho hầu hết các ví dụ ở đây và không chạy chúng qua trình bao proxy như tôi đã làm với sh <<-\CMD
.
Ok, như tôi đã nêu trong các ý kiến trên, tôi không biết cụ thể về bashset -E
, nhưng tôi biết rằng các vỏ tương thích POSIX cung cấp một phương tiện đơn giản để kiểm tra giá trị nếu bạn muốn:
sh -evx <<-\CMD
_test() { echo $( ${empty:?error string} ) &&\
echo "echo still works"
}
_test && echo "_test doesnt fail"
# END
CMD
sh: line 1: empty: error string
+ echo
+ echo 'echo still works'
echo still works
+ echo '_test doesnt fail'
_test doesnt fail
Ở trên bạn sẽ thấy rằng mặc dù tôi đã từng parameter expansion
kiểm tra ${empty?} _test()
vẫn return
là một lượt - như đã được chứng minh trong lần trước echo
Điều này xảy ra bởi vì giá trị không thành công sẽ giết chết lớp $( command substitution )
vỏ con chứa nó, nhưng vỏ mẹ của nó - _test
tại thời điểm này - vẫn tiếp tục vận chuyển. Và echo
không quan tâm - đó là rất nhiều hạnh phúc chỉ để phục vụ một \newline; echo
là không kiểm tra.
Nhưng hãy xem xét điều này:
sh -evx <<-\CMD
_test() { echo $( ${empty:?error string} ) &&\
echo "echo still works" ; } 2<<-INIT
${empty?function doesnt run}
INIT
_test ||\
echo "this doesnt even print"
# END
CMD
_test+ sh: line 1: empty: function doesnt run
Bởi vì tôi đã cung cấp _test()'s
đầu vào với một tham số được đánh giá trước trong INIT here-document
lúc này, _test()
hàm thậm chí không cố chạy. Những gì nhiều hơn sh
vỏ rõ ràng từ bỏ hoàn toàn con ma và echo "this doesnt even print"
thậm chí không in.
Có lẽ đó không phải là điều bạn muốn.
Điều này xảy ra bởi vì ${var?}
mở rộng tham số kiểu được thiết kế để thoát khỏishell
trường hợp thiếu tham số, nó hoạt động như sau :
${parameter:?[word]}
Lỗi chỉ ra nếu Null
hoặc Unset.
Nếu tham số không được đặt hoặc null, expansion of word
(hoặc một thông báo cho biết nó không được đặt nếu từ bị bỏ qua) sẽ là written to standard error
và shell exits with a non-zero exit status
. Nếu không, giá trị của parameter shall be substituted
. Một vỏ tương tác không cần phải thoát.
Tôi sẽ không sao chép / dán toàn bộ tài liệu, nhưng nếu bạn muốn thất bại với set but null
giá trị, bạn sử dụng biểu mẫu:
${var
:? error message }
Với những điều :colon
như trên. Nếu bạn muốn một null
giá trị thành công, chỉ cần bỏ qua dấu hai chấm. Bạn cũng có thể phủ nhận nó và chỉ thất bại đối với các giá trị được đặt, như tôi sẽ hiển thị trong giây lát.
Một hoạt động khác của _test():
sh <<-\CMD
_test() { echo $( ${empty:?error string} ) &&\
echo "echo still works" ; } 2<<-INIT
${empty?function doesnt run}
INIT
echo "this runs" |\
( _test ; echo "this doesnt" ) ||\
echo "now it prints"
# END
CMD
this runs
sh: line 1: empty: function doesnt run
now it prints
Điều này hoạt động với tất cả các loại thử nghiệm nhanh, nhưng ở trên bạn sẽ thấy rằng _test()
, chạy từ giữa pipeline
thất bại, và trên thực tế, nó chứa phần tử con của nó command list
hoàn toàn thất bại, vì không có lệnh nào trong hàm chạy hay echo
chạy sau , mặc dù nó cũng được hiển thị rằng nó có thể dễ dàng được kiểm tra bởi vì echo "now it prints"
bây giờ in.
Ma quỷ là trong các chi tiết, tôi đoán. Trong trường hợp trên, lớp vỏ thoát ra không phải là tập lệnh _main | logic | pipeline
mà là ( subshell in which we ${test?} ) ||
một hộp cát nhỏ được yêu cầu.
Và nó có thể không rõ ràng, nhưng nếu bạn muốn chỉ vượt qua cho trường hợp ngược lại, hoặc chỉ set=
các giá trị, nó cũng khá đơn giản:
sh <<-\CMD
N= #N is NULL
_test=$N #_test is also NULL and
v="something you would rather do without"
( #this subshell dies
echo "v is ${v+set}: and its value is ${v:+not NULL}"
echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
${_test:+${N:?so you test for it with a little nesting}}
echo "sure wish we could do some other things"
)
( #this subshell does some other things
unset v #to ensure it is definitely unset
echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
${_test:+${N:?is never substituted}}
echo "so now we can do some other things"
)
#and even though we set _test and unset v in the subshell
echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
# END
CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without
Ví dụ trên tận dụng lợi thế của cả 4 hình thức thay thế tham số POSIX và các thử nghiệm :colon null
hoặc khác nhau của chúng not null
. Có nhiều thông tin hơn trong liên kết ở trên, và đây là một lần nữa .
Và tôi đoán chúng ta cũng nên thể hiện _test
chức năng của mình, phải không? Chúng tôi chỉ khai báo empty=something
như một tham số cho chức năng của chúng tôi (hoặc bất kỳ lúc nào trước đó):
sh <<-\CMD
_test() { echo $( echo ${empty:?error string} ) &&\
echo "echo still works" ; } 2<<-INIT
${empty?tested as a pass before function runs}
INIT
echo "this runs" >&2 |\
( empty=not_empty _test ; echo "yay! I print now!" ) ||\
echo "suspiciously quiet"
# END
CMD
this runs
not_empty
echo still works
yay! I print now!
Cần lưu ý rằng đánh giá này là độc lập - nó không yêu cầu thử nghiệm bổ sung để thất bại. Một vài ví dụ nữa:
sh <<-\CMD
empty=
${empty?null, no colon, no failure}
unset empty
echo "${empty?this is stderr} this is not"
# END
CMD
sh: line 3: empty: this is stderr
sh <<-\CMD
_input_fn() { set -- "$@" #redundant
echo ${*?WHERES MY DATA?}
#echo is not necessary though
shift #sure hope we have more than $1 parameter
: ${*?WHERES MY DATA?} #: do nothing, gracefully
}
_input_fn heres some stuff
_input_fn one #here
# shell dies - third try doesnt run
_input_fn you there?
# END
CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?
Và cuối cùng chúng ta quay trở lại câu hỏi ban đầu: làm thế nào để xử lý lỗi trong một mạng con $(command substitution)
? Sự thật là - có hai cách, nhưng không phải là trực tiếp. Cốt lõi của vấn đề là quá trình đánh giá của shell - việc mở rộng shell (bao gồm $(command substitution)
) xảy ra sớm hơn trong quy trình đánh giá của shell so với thực thi lệnh shell hiện tại - đó là khi lỗi của bạn có thể bị bắt và bị mắc kẹt.
Vấn đề mà các trải nghiệm op là vào thời điểm shell hiện tại đánh giá lỗi, $(command substitution)
lớp con đã được thay thế - không còn lỗi.
Vậy hai cách là gì? Hoặc bạn thực hiện nó một cách rõ ràng trong phạm vi con $(command substitution)
với các thử nghiệm mà bạn không có nó hoặc bạn hấp thụ kết quả của nó vào một biến shell hiện tại và kiểm tra giá trị của nó.
Cách 1:
echo "$(madeup && echo \: || echo '${fail:?die}')" |\
. /dev/stdin
sh: command not found: madeup
/dev/stdin:1: fail: die
echo $?
126
Cách 2:
var="$(madeup)" ; echo "${var:?die} still not stderr"
sh: command not found: madeup
sh: var: die
echo $?
1
Điều này sẽ thất bại bất kể số lượng biến được khai báo trên mỗi dòng:
v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"
sh: command not found: madeup
sh: v1: parameter not set
Và giá trị trả lại của chúng tôi không đổi:
echo $?
1
BÂY GIỜ
trap 'printf %s\\n trap resurrects shell!' ERR
v1="$(madeup)" v2="$(printf %s\\n shown after trap)"
echo "${v1:?#1 - still stderr}" "${v2:?invisible}"
sh: command not found: madeup
sh: v1: #1 - still stderr
trap
resurrects
shell!
shown
after
trap
echo $?
0
echo $( made up name )
bằng$( made up name )
sản xuất các hành vi mong muốn. Tôi không có một lời giải thích mặc dù.