是否有类似于Java中的try catch finally的Linux bash命令?还是Linux shell总是继续执行下去?
try {
`executeCommandWhichCanFail`
mv output
} catch {
mv log
} finally {
rm tmp
}
是否有类似于Java中的try catch finally的Linux bash命令?还是Linux shell总是继续执行下去?
try {
`executeCommandWhichCanFail`
mv output
} catch {
mv log
} finally {
rm tmp
}
根据您的示例,看起来您正在尝试执行类似于始终删除临时文件的操作,无论脚本如何退出。在Bash中,可以使用trap
内置命令来捕获EXIT
信号以实现此目的。
#!/bin/bash
trap 'rm tmp' EXIT
if executeCommandWhichCanFail; then
mv output
else
mv log
exit 1 #Exit with failure
fi
exit 0 #Exit with success
在 trap
中的 rm tmp
语句总是在脚本退出时执行,因此文件 "tmp" 总是会尝试被删除。
已安装的陷阱也可以重置;仅使用信号名称调用 trap 将重置信号处理程序。
trap EXIT
更多详细信息,请参阅Bash手册页面:man bash
trap 'rm tmp' EXIT SIGINT
。 - ishmaelEXIT
处理程序也会在接收到SIGINT
和SIGTERM
信号时被调用。 - Cuadueif/else
或 ||
方法相比的优势,并且更像 C++ 或 Java 中的 try
/catch
。 - Toby Speight嗯,有点类似:
{ # your 'try' block
executeCommandWhichCanFail &&
mv output
} || { # your 'catch' block
mv log
}
rm tmp # finally: this will always happen
executeCommandWhichCanFail
后,需要使用&&
,否则程序会继续执行。即使在其前面加上set -e
(我不理解这一点)也是如此。 - AJPtrap
,因为||
不能确保在异常情况(信号)下执行另一部分,而这几乎是人们从finally
期望的。 - Petr(...)
而不是 {...}
,这样你就不必在每一行上都使用 && 了。 - Alexander Mills( )
子shell组也适用,尽管-e通常会传播到子shell。 - rakslice我在脚本中使用以下语法取得了成功:
# Try, catch, finally
(echo "try this") && (echo "and this") || echo "this is the catch statement!"
# this is the 'finally' statement
echo "finally this"
如果任何一个try语句抛出错误或以exit 1
结束,则解释器将转到catch语句,然后再执行finally语句。
如果两个try语句都成功(和/或以exit
结束),则解释器将跳过catch语句,然后运行finally语句。
示例_1:
goodFunction1(){
# this function works great
echo "success1"
}
goodFunction2(){
# this function works great
echo "success2"
exit
}
(goodFunction1) && (goodFunction2) || echo "Oops, that didn't work!"
echo "Now this happens!"
输出_1
success1
success2
Now this happens!
示例_2
functionThrowsErr(){
# this function returns an error
ech "halp meh"
}
goodFunction2(){
# this function works great
echo "success2"
exit
}
(functionThrowsErr) && (goodFunction2) || echo "Oops, that didn't work!"
echo "Now this happens!"
输出_2
main.sh: line 3: ech: command not found
Oops, that didn't work!
Now this happens!
示例三
functionThrowsErr(){
# this function returns an error
echo "halp meh"
exit 1
}
goodFunction2(){
# this function works great
echo "success2"
}
(functionThrowsErr) && (goodFunction2) || echo "Oops, that didn't work!"
echo "Now this happens!"
输出_3
halp meh
Oops, that didn't work!
Now this happens!
(functionThrowsErr) || echo "Oops, functionThrowsErr didn't work!"
(goodFunction2) || echo "Oops, good function is bad"
echo "Now this happens!"
输出
halp meh
Oops, functionThrowsErr didn't work!
success2
Now this happens!
mv
命令需要两个参数,因此您可能真正想要的是使用 cat
命令查看输出文件的内容:
echo `{ execCommand && cat output ; } || cat log`
rm -f tmp
另一种方法是:
set -e; # stop on errors
mkdir -p "$HOME/tmp/whatevs"
exit_code=0
(
set +e;
(
set -e;
echo 'foo'
echo 'bar'
echo 'biz'
)
exit_code="$?"
)
rm -rf "$HOME/tmp/whatevs"
if [[ "exit_code" != '0' ]]; then
echo 'failed';
fi
虽然上述内容并没有比以下内容更有优势:
set -e; # stop on errors
mkdir -p "$HOME/tmp/whatevs"
exit_code=0
(
set -e;
echo 'foo'
echo 'bar'
echo 'biz'
exit 44;
exit 43;
) || {
exit_code="$?" # exit code of last command which is 44
}
rm -rf "$HOME/tmp/whatevs"
if [[ "exit_code" != '0' ]]; then
echo 'failed';
fi
警告:退出陷阱并非总是被执行。自从我写下这个答案以来,我遇到了一些情况,在这些情况下,我的退出陷阱不会被执行,导致文件丢失,但我还没有找到原因。
问题出现在我使用Ctrl+C
停止Python脚本时,Python脚本又执行了一个使用退出陷阱的Bash脚本,实际上应该导致退出陷阱在Bash中被执行,因为退出陷阱在Bash中是在SIGINT
信号时被执行的。
因此,虽然trap .. exit
对于清理很有用,但仍有一些场景无法执行,最明显的是停电和接收到SIGKILL
信号。
我经常发现随着我添加额外选项或进行其他更改,bash脚本变得越来越大。当一个bash脚本包含许多函数时,使用“trap EXIT”可能会变得不太容易。
例如,考虑作为调用的脚本
dotask TASK [ARG ...]
每个 TASK
可能包含子步骤,在这种情况下,希望在中间执行清理操作。
在这种情况下,使用子 shell 生成作用域退出陷阱是有帮助的,例如:
function subTask (
local tempFile=$(mktemp)
trap "rm '${tempFile}'" exit
...
)
然而,与子shell一起工作可能会很棘手,因为它们无法设置父shell的全局变量。
此外,编写单个退出陷阱通常是不方便的。例如,清理步骤可能取决于函数在遇到错误之前走了多远。能够进行RAII样式的清理声明将是很好的选择:
function subTask (
...
onExit 'rm tmp.1'
...
onExit 'rm tmp.2'
...
)
使用类似的东西似乎是显而易见的
handlers=""
function onExit { handlers+="$1;"; trap "$handlers" exit; }
为了更新陷阱,但这对于嵌套的子shell来说是失败的,因为它会导致父shell处理程序的过早执行。客户端代码必须在子shell开始时显式重置handlers
变量。
在[多个bash陷阱使用相同的信号]中讨论的解决方案,通过使用trap -p EXIT
的输出来修补陷阱,同样会失败:尽管子shell不继承EXIT
陷阱,trap -p exit
将显示父shell的处理程序,因此需要手动重置。