在Bash脚本退出之前如何运行一个命令?

173
如果一个Bash脚本使用了set -e,并且脚本中的某个命令返回了错误,我该如何在脚本退出前进行一些清理操作?
例如:
#!/bin/bash
set -e
mkdir /tmp/foo
# ... do stuff ...
rm -r /tmp/foo

如果在... do stuff ...中的某个命令失败,我该如何确保/tmp/foo被删除?

5个回答

289

以下是使用 trap 的示例:

#!/bin/bash -e

function cleanup {
  echo "Removing /tmp/foo"
  rm  -r /tmp/foo
}

trap cleanup EXIT
mkdir /tmp/foo
asdffdsa #Fails

输出:

dbrown@luxury:~ $ sh traptest
t: line 9: asdffdsa: command not found
Removing /tmp/foo
dbrown@luxury:~ $

请注意,尽管 asdffdsa 行失败了,但清理仍然被执行。

3
敬启者:当有人使用Ctrl+D或“exit”结束当前的bash会话时,这也将被执行。 - Qqwy
1
使用 trap cleanup EXIT,当脚本中的命令没有返回错误时, cleanup 函数也会执行.... 另一方面,如果使用例如 trap cleanup ERR,则只有在出现错误时才会执行 cleanup 函数,如果没有错误,则不会执行。更多信息请参见 https://dev59.com/XYTba4cB1Zd3GeqP1Bfr#26261518。 - Abdull

16

根据内置命令 bash 的 man 页面(关于内置命令):

trap [-lp] [[arg] sigspec ...]
当 shell 接收到信号 sigspec 时,将读取并执行命令 arg。

因此,如Anon.所述的答案,在脚本早期调用 trap ,以设置您想要的 ERR 处理程序。


2
运行 help trap 以查看有关内置函数的帮助。 - Flimm

14

sh 版的 devguydavid 的 答案

#!/bin/sh
set -e
cleanup() {
  echo "Removing /tmp/foo"
  rm  -r /tmp/foo
}
trap cleanup EXIT
mkdir /tmp/foo
asdffdsa #Fails

参考:shellscript.sh


1
POSIX 让我微笑。 :) 你链接的教学网站也很棒。 - Cometsong

10

set 的参考文档中:

-e

如果一个简单命令(参见 3.2.1 简单命令)返回非零状态,则立即退出,除非失败的命令是 until 或 while 循环的一部分、if 语句的一部分、&& 或 || 列表的一部分,或者使用!反转了命令的返回状态。如果设置了 ERR 的 trap,则在 shell 退出前执行。

(重点是我的)


2
在使用“-e”时,添加“-E”可能是值得的,参见https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/。 - Max Barraclough
就 FWIW 而言,该陷阱也会在常规脚本退出时运行,即在正常情况下。 - nteissler

1

我不知道这对你来说是在之前还是之后更重要。

您可以使用“trap”设置脚本在关闭后立即运行,然后可以关闭终端。

例如:

    trap  ./val.sh EXIT  #set command you want to run after close terminal 
    kill -9 $PPID        #kill current terminal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接