หมายเหตุ: zsh
จะบ่นเกี่ยวกับ "รูปแบบที่ไม่ดี" หากคุณไม่ได้กำหนดค่าให้ยอมรับ "ความคิดเห็นแบบอินไลน์" สำหรับตัวอย่างส่วนใหญ่ที่นี่และไม่เรียกใช้ผ่านพร็อกซีเชลล์ที่ฉันได้ทำsh <<-\CMD
ไป
ตกลงดังนั้นตามที่ฉันระบุไว้ในความคิดเห็นด้านบนฉันไม่รู้เกี่ยวกับทุบตีset -E
โดยเฉพาะแต่ฉันรู้ว่าเปลือกที่เข้ากันได้ POSIX ให้วิธีง่ายๆในการทดสอบค่าถ้าคุณต้องการ:
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
ด้านบนคุณจะเห็นว่าแม้ว่าผมจะนำมาใช้parameter expansion
ในการทดสอบ${empty?} _test()
ยังคง return
sผ่าน - ตามที่แจ้งในช่วงecho
นี้เกิดขึ้นเนื่องจากค่าล้มเหลวฆ่า$( command substitution )
subshell ที่มี แต่เปลือกแม่ - _test
ในขณะนี้ - ช่วยในการขนส่ง และecho
ไม่ได้ดูแล - มันมีความสุขมากมายที่จะทำหน้าที่เพียง\newline; echo
เป็นไม่ได้ทดสอบ
แต่ให้พิจารณาสิ่งนี้:
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
เนื่องจากฉันป้อน_test()'s
อินพุตด้วยพารามิเตอร์ที่ประเมินล่วงหน้าในINIT here-document
ตอนนี้_test()
ฟังก์ชั่นจึงไม่แม้แต่จะพยายามเลยแม้แต่น้อย sh
เห็นได้ชัดว่ามีเปลือกมากกว่าให้ผีทั้งหมดและecho "this doesnt even print"
ไม่ได้พิมพ์
อาจไม่ใช่สิ่งที่คุณต้องการ
สิ่งนี้เกิดขึ้นเนื่องจากการ${var?}
ขยายพารามิเตอร์สไตล์ถูกออกแบบมาเพื่อออกจากshell
ในกรณีที่พารามิเตอร์หายไปมันทำงานเช่นนี้ :
${parameter:?[word]}
บ่งชี้ถึงข้อผิดพลาดถ้าNull
หรือUnset.
ถ้าพารามิเตอร์ถูก unset หรือ null ที่expansion of word
(หรือข้อความที่บ่งชี้ว่าไม่มีการตั้งค่าถ้าคำที่ถูกละไว้) ต้องและwritten to standard error
มิฉะนั้นค่าของ เชลล์แบบโต้ตอบไม่จำเป็นต้องออกshell exits with a non-zero exit status
parameter shall be substituted
ฉันจะไม่คัดลอก / วางเอกสารทั้งหมด แต่ถ้าคุณต้องการความล้มเหลวสำหรับset but null
ค่าที่คุณใช้แบบฟอร์ม:
${var
:? error message }
ด้วย:colon
ดังกล่าวข้างต้น หากคุณต้องการnull
ค่าที่จะประสบความสำเร็จเพียงละเว้นลำไส้ใหญ่ คุณสามารถลบล้างมันและล้มเหลวเฉพาะกับค่าที่ตั้งไว้เพราะฉันจะแสดงในช่วงเวลาหนึ่ง
วิ่งอีกของ _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
งานนี้กับทุกชนิดของการทดสอบอย่างรวดเร็ว แต่ข้างต้นคุณจะเห็นว่า_test()
การเรียกใช้จากตรงกลางของที่pipeline
ล้มเหลวและในความเป็นจริงมีของcommand list
subshell ล้มเหลวอย่างสิ้นเชิงที่ไม่มีคำสั่งภายในระยะฟังก์ชันมิได้ดังต่อไปนี้echo
การทำงานที่ทุกคน แม้ว่ามันจะแสดงให้เห็นว่ามันสามารถทดสอบได้ง่ายเพราะecho "now it prints"
ตอนนี้พิมพ์
ฉันคิดว่าปีศาจอยู่ในรายละเอียด ในกรณีข้างต้นเชลล์ที่ออกไม่ได้เป็นสคริปต์_main | logic | pipeline
แต่( subshell in which we ${test?} ) ||
มีการเรียกใช้แซนด์บ็อกซ์เพียงเล็กน้อย
และอาจไม่ชัดเจน แต่ถ้าคุณต้องการส่งเฉพาะกรณีตรงข้ามหรือset=
ค่าเท่านั้นมันค่อนข้างง่ายเช่นกัน:
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
ตัวอย่างข้างต้นใช้ประโยชน์จากการแทนที่พารามิเตอร์ POSIX ทั้ง 4 รูปแบบและการทดสอบ:colon null
หรือnot null
การทดสอบที่หลากหลาย มีข้อมูลเพิ่มเติมในการเชื่อมโยงดังกล่าวข้างต้นและที่นี่ก็เป็นอีกครั้ง
และฉันคิดว่าเราควรแสดง_test
ฟังก์ชั่นการทำงานของเราด้วยใช่ไหม เราเพิ่งประกาศempty=something
ว่าเป็นพารามิเตอร์สำหรับฟังก์ชั่นของเรา (หรือก่อนเวลาใด ๆ ):
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!
ควรสังเกตว่าการประเมินนี้อยู่คนเดียว - ไม่จำเป็นต้องทดสอบเพิ่มเติมเพื่อล้มเหลว อีกสองสามตัวอย่าง:
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?
ในที่สุดเราก็กลับมาที่คำถามเดิม: จะจัดการข้อผิดพลาดใน$(command substitution)
subshell ได้อย่างไร? ความจริงก็คือ - มีสองวิธี แต่ไม่มีทางตรง หลักของปัญหาคือกระบวนการประเมินของเชลล์ - การขยายเชลล์ (รวมถึง$(command substitution)
) เกิดขึ้นก่อนหน้านี้ในกระบวนการประเมินของเชลล์มากกว่าการดำเนินการคำสั่งเชลล์ปัจจุบัน - ซึ่งเมื่อข้อผิดพลาดของคุณอาจถูกดักจับและติดอยู่
ปัญหาที่พบจาก op คือเมื่อถึงเวลาที่เชลล์ปัจจุบันประเมินหาข้อผิดพลาด$(command substitution)
subshell นั้นถูกแทนที่แล้ว - ไม่มีข้อผิดพลาดเหลืออยู่
ดังนั้นสองวิธีคืออะไร ไม่ว่าคุณจะทำอย่างชัดเจนภายใน$(command substitution)
subshell ด้วยการทดสอบตามที่คุณต้องการโดยไม่มีหรือคุณดูดซับผลลัพธ์ลงในตัวแปร shell ปัจจุบันและทดสอบค่าของมัน
วิธีที่ 1:
echo "$(madeup && echo \: || echo '${fail:?die}')" |\
. /dev/stdin
sh: command not found: madeup
/dev/stdin:1: fail: die
echo $?
126
วิธีที่ 2:
var="$(madeup)" ; echo "${var:?die} still not stderr"
sh: command not found: madeup
sh: var: die
echo $?
1
สิ่งนี้จะล้มเหลวโดยไม่คำนึงถึงจำนวนตัวแปรที่ประกาศต่อบรรทัด:
v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"
sh: command not found: madeup
sh: v1: parameter not set
และค่าตอบแทนของเรายังคงที่:
echo $?
1
ตอนนี้การดักฟัง:
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 )
ด้วยการ$( made up name )
สร้างพฤติกรรมที่ต้องการ ฉันไม่มีคำอธิบายว่า