Shell脚本中set -e和set -o pipefail的作用

前言

bash 复制代码
set -e
set -o pipefail

这个世界很多人的狂妄源于他并不知道自己不知道,所以才有了那句话:知道的越多实际上不知道的越多,今天文章的开头便甩了两行代码,这很不常见,本来这两天在忙着ClickHouse的搭建,想着搞完总结一下,但是牵扯的东西太多,所以先拿出今天发现的这个亮点记录一下,原来脚本还可以这么写。

set -e 的作用

只要脚本中有任意命令返回非 0 状态(失败),脚本就会立即退出,可以看下面这个例子

bash 复制代码
echo 1
cp not_exist_file /tmp/   # 假设这句失败
echo 2
  • 有 set -e:脚本执行到 cp 就马上退出,不会执行 echo 2
  • 没有 set -e:即使 cp 失败,脚本仍会继续执行 echo 2

它的意义就是防止错误被忽略,避免执行后续危险操作,特别适合以下场景

  • 下载失败不能继续
  • 解压失败不能继续
  • 数据导入失败不能继续

如果不用 set -e,为了阻止错误后继续执行,就得向下面这样写了

bash 复制代码
some_command
if [ $? -ne 0 ]; then
    echo "some_command failed"
    exit 1
fi

some_command || exit 1

some_command || { echo "failed"; exit 1; }

但是要注意,set -e 并不是对所有命令都触发,if 条件判断中的命令失败不会触发退出,比如下面这个命令执行错误

bash 复制代码
if grep keyword file.txt; then
   ...
fi

使用 set -e 面临的问题

开启 set -e 后,脚本自动退出,那我要如何打印出"是什么命令失败了"、"失败的原因"呢?

实际上,set -e 自己不会打印任何错误信息,只会在某个命令失败时直接退出脚本

有些命名失败时自己会输出的信息,比如 cp file1 file2 失败通常会打印 cp: cannot stat 'file1': No such file or directory

而专业的做法可以使用 trap 捕获错误并打印日志,比如可以在脚本开头加上这样一句

bash 复制代码
#!/bin/bash
set -e

trap 'echo "[ERROR] Command failed at line $LINENO: $BASH_COMMAND"' ERR

试验一下

编写一下测试脚本,执行

bash 复制代码
#!/bin/bash
set -e
set -o pipefail

trap 'echo "[ERROR] Command failed at line $LINENO: $BASH_COMMAND"' ERR

rm not_exist_file

运行结果如下:

bash 复制代码
$ ./testtrap.sh
rm: cannot remove 'not_exist_file': No such file or directory
[ERROR] Command failed at line 7: rm not_exist_file

除了rm命令本来的提示,trap给出了自定义提示,可以更加人性化

set -o pipefail 的作用

让管道命令的退出状态由"最右边命令"变成"整个管道中第一个失败的命令",使错误暴露出来不至于隐藏不见

示例代码如下

bash 复制代码
cat file.txt | grep "keyword"

假设:

  • cat file.txt 失败(文件不存在)
  • grep 依然成功(因为输入为空)

默认行为(不加 pipefail):

整个管道返回值是 grep 的,而 grep 是成功的 → 脚本认为整条命令成功

加上 pipefail 后:

整个命令返回 "cat file.txt 的失败",脚本能正确判断管道失败

结论

  • set -e 的作用是只要脚本中有任意命令返回非 0 状态(失败),脚本就会立即退出,避免继续执行引发更大错误
  • set -o pipefail 的作用是让管道命令的退出状态由"最右边命令"变成"整个管道中第一个失败的命令",避免某些管道错误会被悄悄忽略
  • 有些方案不知道时根本就不知道自己不知道,知道了就发现居然还有这种解法

==>> 反爬链接,请勿点击,原地爆炸,概不负责!<<==


有些事就是那么巧,在我考虑将大量日志从MySQL迁移出时,评估了ClickHouse和Hive两个方向,最终选择了ClickHouse,之后突然发现合作商的SQL权限今天开了,一查他们也是ClickHouse,想到这里找人用同样的方法去查询了老东家的解决方案,居然是Hive,难道是英雄所见略同?哈哈~

相关推荐
Lsetea20 小时前
OpenSSL s_client退出码为0却证书验证失败:严格校验与Shell管道排查
https·shell·ssl证书·openssl·tls
fox_charon2 天前
Windows 下 CLI 参数的引号陷阱:为什么 --resume 'uuid' 会失败
windows·shell·cmd·cli·引号
吴声子夜歌11 天前
Shell编程实例——编写安全的shell脚本(二)
linux·运维·shell
吴声子夜歌13 天前
Shell编程实例——内务及管理任务(一)
linux·运维·shell
吴声子夜歌13 天前
Shell编程实例——高级脚本编程(一)
linux·运维·网络·shell
吴声子夜歌13 天前
Shell编程实例——bash的配置与自定义(二)
linux·运维·shell
吴声子夜歌14 天前
Shell编程实例——与解析相关的任务(二)
linux·运维·shell
吴声子夜歌14 天前
Shell编程实例——脚本编程的附加特性
linux·运维·shell
云计算练习生14 天前
什么是内核?操作系统内核到底管哪些事
linux·windows·操作系统·内核·shell
吴声子夜歌17 天前
Shell编程实例——bash入门
linux·运维·shell