我如何获得“的效果和有用性?”;集合-e";在shell函数中?
我如何获得“的效果和有用性?”;集合-e";在shell函数中?,shell,error-handling,sh,Shell,Error Handling,Sh,set-e(或以#!/bin/sh-e开头的脚本)对于在出现问题时自动弹出非常有用。这样我就不用对可能失败的每个命令进行错误检查了 如何在一个函数中得到它的等价物 例如,我有以下脚本,在出现错误时立即退出,并具有错误退出状态: #!/bin/sh -e echo "the following command could fail:" false echo "this is after the command that fails" 产出如预期: the following command c
set-e
(或以#!/bin/sh-e开头的脚本)对于在出现问题时自动弹出非常有用。这样我就不用对可能失败的每个命令进行错误检查了
如何在一个函数中得到它的等价物
例如,我有以下脚本,在出现错误时立即退出,并具有错误退出状态:
#!/bin/sh -e
echo "the following command could fail:"
false
echo "this is after the command that fails"
产出如预期:
the following command could fail:
现在我想把它包装成一个函数:
#!/bin/sh -e
my_function() {
echo "the following command could fail:"
false
echo "this is after the command that fails"
}
if ! my_function; then
echo "dealing with the problem"
fi
echo "run this all the time regardless of the success of my_function"
f() { sh -ec '
echo This will execute
false
echo This will not
'
}
预期产出:
the following command could fail:
dealing with the problem
run this all the time regardless of the success of my_function
实际产量:
the following output could fail:
this is after the command that fails
run this all the time regardless of the success of my_function
(即,该函数正在忽略set-e
)
这大概是预期的行为。我的问题是:如何在shell函数中获得set-e
的效果和有用性?我希望能够设置一些东西,这样我就不必逐个检查每个调用的错误,但是脚本在遇到错误时将停止。它应该根据需要将堆栈展开,直到我检查结果为止,或者如果我没有检查,则退出脚本本身。这是set-e
已经做过的,只是它没有嵌套
我发现堆栈溢出之外有问题,但没有合适的答案。使用&&
操作符将函数中的所有命令连接起来。这不会太麻烦,会给你想要的结果。我知道这不是你所要求的,但你可能知道也可能不知道你所追求的行为是建立在“制造”中的。“make”进程中任何失败的部分都会中止运行。不过,这是一种与shell脚本完全不同的“编程”方式。来自set-e
的文档:
启用此选项时,如果对任何
原因列在事故后果表中
Shell错误或返回退出状态
值>0,并且不属于
在while
之后的复合列表,
直到
,或如果
关键字,并且不是
属于和
或或
列表的一部分,并且不是
前面有的管道代码>保留
一句话,那么外壳就马上
退出
在您的例子中,false
是管道的一部分,前面有代码>和的一部分(如果)。因此,解决方案是重写代码,使其不受影响
换句话说,函数在这里没有什么特别之处。尝试:
set -e
! { false; echo hi; }
您需要在子shell(括号内)中调用函数来实现这一点
我想你应该这样写剧本:
#!/bin/sh -e
my_function() {
echo "the following command could fail:"
false
echo "this is after the command that fails"
}
(my_function)
if [ $? -ne 0 ] ; then
echo "dealing with the problem"
fi
echo "run this all the time regardless of the success of my_function"
然后输出为(根据需要):
这有点困难,但您可以:
export -f f
if sh -ec f; then
...
出口-f
如果sh-ecf;然后
...
如果您的shell支持export-f(bash支持)
请注意,这不会终止脚本。回声
f中的false之后将不会执行,主体也不会执行
但将执行if之后的语句
如果您使用的shell不支持export-f,则可以
通过在函数中运行sh获得所需的语义:
#!/bin/sh -e
my_function() {
echo "the following command could fail:"
false
echo "this is after the command that fails"
}
if ! my_function; then
echo "dealing with the problem"
fi
echo "run this all the time regardless of the success of my_function"
f() { sh -ec '
echo This will execute
false
echo This will not
'
}
f(){sh-ec'
这将执行
假的
呼应这一点是不会的
'
}
我最终选择了这个,这显然是可行的。起初我尝试了导出方法,但后来发现我需要导出脚本使用的每个全局(常量)变量
禁用set-e
,然后在启用了set-e
的子shell中运行函数调用。将子shell的退出状态保存在变量中,重新启用set-e,然后测试var
f() { echo "a"; false; echo "Should NOT get HERE"; }
# Don't pipe the subshell into anything or we won't be able to see its exit status
set +e ; ( set -e; f ) ; err_status=$?
set -e
## cleaner syntax which POSIX sh doesn't support. Use bash/zsh/ksh/other fancy shells
if ((err_status)) ; then
echo "f returned false: $err_status"
fi
## POSIX-sh features only (e.g. dash, /bin/sh)
if test "$err_status" -ne 0 ; then
echo "f returned false: $err_status"
fi
echo "always print this"
不能将f
作为管道的一部分,或作为|
命令列表的&
的一部分运行(管道或列表中的最后一个命令除外),或作为if
或while
中的条件运行,或作为忽略set-e
的其他上下文运行此代码也不能在这些上下文中使用,因此如果在函数中使用此代码,调用方必须使用相同的子shell/保存退出状态欺骗。鉴于限制和难以阅读的语法,将set-e
用于类似于抛出/捕获异常的语义并不真正适合于一般使用
trap err\u handler\u function err
与set-e
具有相同的限制,即在命令失败时set-e
不会退出的上下文中,它不会因错误而触发
您可能认为以下方法可行,但事实并非如此:
if ! ( set -e; f );then ##### doesn't work, f runs ignoring -e
echo "f returned false: $?"
fi
set-e
不会在子shell内生效,因为它记住它在if
的条件内。我认为作为子shell会改变这一点,但只有在一个单独的文件中并在其上运行一个完整的单独shell才能起作用。您可以直接使用子shell作为函数定义,并使用set-e
将其设置为立即退出。这会将set-e
的范围仅限于函数子shell,并在以后避免在set+e
和set-e
之间切换
此外,您可以在if
测试中使用变量赋值,然后在附加的else
语句中回显结果
# use subshell for function definition
f() (
set -exo pipefail
echo a
false
echo Should NOT get HERE
exit 0
)
# next line also works for non-subshell function given by agsamek above
#if ret="$( set -e && f )" ; then
if ret="$( f )" ; then
true
else
echo "$ret"
fi
# prints
# ++ echo a
# ++ false
# a
这是通过设计和POSIX规范实现的。我们可以在manbash
中阅读:
如果复合命令或外壳函数在忽略-e
的上下文中执行,则即使设置了-e
且命令返回故障状态,复合命令或函数体中执行的任何命令都不会受到-e
设置的影响。如果复合命令或shell函数在忽略-e
的上下文中执行时设置了-e
,则在复合命令或包含函数调用的命令完成之前,该设置不会产生任何效果
因此,您应该避免在函数中依赖set-e
给出以下示例:
函数中的set-e
被忽略,因为函数是逗号
#!/bin/sh
set -e
my_function() {
echo "the following command could fail:"
false || return $?
echo "this is after the command that fails"
}
if ! my_function; then
echo "dealing with the problem"
fi
echo "run this all the time regardless of the success of my_function"
the following command could fail:
dealing with the problem
run this all the time regardless of the success of my_function
#!/bin/bash
# Get rid of that disgusting set -e. We don't need it anymore!
# functrace allows RETURN and DEBUG traps to be inherited by each
# subshell and function. Plus, it doesn't suffer from that horrible
# erasure problem that -e and -E suffer from when the command
# is used in a conditional expression.
set -o functrace
# A trap to bubble up the error unless our magic command is encountered
# ('catch=$?' in this case) at which point it stops. Also don't try to
# bubble the error if were not in a function.
trap '{
code=$?
if [[ $code != 0 ]] && [[ $BASH_COMMAND != '\''catch=$?'\'' ]]; then
# If were in a function, return, else exit.
[[ $FUNCNAME ]] && return $code || exit $code
fi
}' DEBUG
my_function() {
my_function2
}
my_function2() {
echo "the following command could fail:"
false
echo "this is after the command that fails"
}
# the || isn't necessary, but the 'catch=$?' is.
my_function || catch=$?
echo "Dealing with the problem with errcode=$catch (⌐■_■)"
echo "run this all the time regardless of the success of my_function"
the following command could fail:
Dealing with the problem with errcode=1 (⌐■_■)
run this all the time regardless of the success of my_function