注意:zsh
如果您在此处的大多数示例未将其配置为接受“内联注释”,并且没有像我一样通过代理shell运行它们,则会抱怨“错误模式” sh <<-\CMD
。
好的,所以,正如我在上面的评论中所述,我不特别了解bash的知识set -E
,但是我知道POSIX兼容的shell提供了一种简单的测试值的方法,如果需要的话:
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
š一通-作为明证是,在过去echo
这是因为失败的价值杀死$( command substitution )
包含它的子shell,但它的母贝- _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]}
指示Error if Null
或Unset.
If参数未设置或为null,expansion of word
(或如果省略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
子外壳完全失败了,因为该函数中的任何命令都不会运行,也不会随后的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
上面的例子中利用的所有4所形成POSIX的参数替换和它们的各种: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)
子Shell中处理错误?事实是-有两种方法,但都不是直接的。问题的核心是外壳的评估过程-外壳扩展(包括$(command substitution)
)在shell的评估过程中发生的时间比当前shell命令的执行时间更早-在这种情况下,错误可能会被捕获和捕获。
op遇到的问题是,到当前外壳评估错误时,$(command substitution)
子外壳已被替换掉-没有错误。
那么这两种方式是什么?可以$(command substitution)
像没有它一样在子shell中使用测试显式地执行它,或者将其结果吸收到当前的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 )
产生所需的行为。我没有解释。