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,难道是英雄所见略同?哈哈~

相关推荐
DogDaoDao19 小时前
Windows 双 Shell 全面对比:CMD 与 PowerShell 的技术剖析
windows·shell·测试·powershell·cmd·程序员开发
Lsetea2 天前
OpenSSL的checkhost显示不匹配却退出0:名称检查与证书验收
linux·https·shell·ssl证书·openssl
钱栈up5 天前
耳机音效不对,我顺手查了系统更新,结果挖出个崩溃了整天的服务
windows·shell
qetfw5 天前
grep 文本筛选:匹配、反选与退出状态验证
linux·shell
xbzb12 天前
Shell 函数:语法、传参与递归调用一次讲透
shell·递归·函数·调用·传参·递归调用
东鹏特饮13 天前
15个实用Linux运维脚本
linux·运维·shell·脚本·redhat·常用生产脚本
Zoom13 天前
【开源】7 天,我用 Rust 复刻了 FinalShell!Rhost v1.0.0 发布
rust·electron·shell
Lsetea22 天前
OpenSSL s_client退出码为0却证书验证失败:严格校验与Shell管道排查
https·shell·ssl证书·openssl·tls
fox_charon23 天前
Windows 下 CLI 参数的引号陷阱:为什么 --resume 'uuid' 会失败
windows·shell·cmd·cli·引号
吴声子夜歌1 个月前
Shell编程实例——编写安全的shell脚本(二)
linux·运维·shell