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

相关推荐
阿海5741 天前
安装php7.4.33的shell脚本
php·shell
阿海5742 天前
卸载redis7.2.4的shell脚本
linux·redis·shell
小嘟嘟132 天前
从基础到进阶:掌握 userdel,玩转 Linux 用户管理的 “减法” 艺术
linux·运维·网络·shell
gis分享者2 天前
如何在 Shell 脚本中实现文件的读写操作?(容易)
shell·文件·awk·echo·cat··
eight *3 天前
源码部署docker自动化脚本
docker·shell
gis分享者4 天前
如何在 Shell 脚本中使用管道(pipeline)实现数据传递?(容易)
linux·pipeline·shell·脚本·管道·数据传递
シ風箏7 天前
Shell【脚本 06】监测文件数据量并压缩及查看远程服务器状态并删除文件脚本分享
linux·运维·服务器·github·shell
pingzhuyan7 天前
linux运维异常(总) - 排查与修复(系统yum,docker,网络dns解析等)
linux·运维·docker·centos·shell
问道飞鱼7 天前
【Linux知识】Shell 脚本参数详解:从基础到高级应用
linux·运维·服务器·shell