80

有没有像 java try catch finally 这样的 linux bash 命令?还是 linux shell 总是在运行?

try {
   `executeCommandWhichCanFail`
   mv output
} catch {
    mv log
} finally {
    rm tmp
}
4

6 回答 6

129

根据您的示例,无论脚本如何退出,您似乎都在尝试执行类似于始终删除临时文件的操作。在 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

脚本退出时始终执行中的rm tmp语句trap,因此文件“tmp”将始终尝试删除。

安装的陷阱也可以重置;仅使用信号名称调用 trap 将重置信号处理程序。

trap EXIT

有关更多详细信息,请参阅 bash 手册页:man bash

于 2013-11-20T01:32:15.713 回答
110

好吧,有点:

{ # your 'try' block
    executeCommandWhichCanFail &&
    mv output
} || { # your 'catch' block
    mv log
}

 rm tmp # finally: this will always happen
于 2013-03-27T10:29:10.127 回答
2

我使用以下语法在我的脚本中找到了成功:

# 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!

Example_3

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!

请注意,函数的顺序会影响输出。如果您需要分别尝试和捕获两个语句,请使用两个 try catch 语句。

(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!
于 2020-02-29T18:31:24.480 回答
1

mv接受两个参数,所以你可能真的想 cat 输出文件的内容:

echo `{ execCommand && cat output ; } || cat log`
rm -f tmp
于 2013-03-27T10:30:43.233 回答
0

另一种方法是:

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 
于 2020-01-23T22:12:02.783 回答
0

警告:出口陷阱并不总是被执行。自从写了这个答案以来,我遇到了一些情况,我的退出陷阱不会被执行,导致文件丢失,我还没有找到原因。

当我用 停止 python 脚本时出现了问题Ctrl+C,该脚本又使用退出陷阱执行了 bash 脚本——这实际上应该导致退出陷阱被执行,因为退出陷阱是SIGINT在 bash 中执行的。

因此,虽然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 的处理程序过早执行。客户端代码必须handlers在子 shell 的开头显式重置变量。

[多个 bash traps for the same signal]中讨论的解决方案,使用来自的输出修补陷阱trap -p EXIT同样会失败:即使子 shell 不继承EXIT陷阱,trap -p exit也会显示父 shell 的处理程序,因此需要手动重置.

于 2020-02-18T22:28:01.410 回答