有些开发人员会用Bash来实现很复杂的功能,就像使用别的高级语言一样。他可能觉得自己很牛逼但其他人早就想锤爆他了,Bash的可读性和可维护性远远低于任何高级语言。更要命的是,Bash并没有方便的调试工具和防错机制,出了问题你要排查半天。

在Ruby或者Python等高级语言里,你很容易知道错误是哪行什么类型的错误,还有IDE的Debugger加持。而Bash只能看源码,通过打印log等非常低效的方式调试。

本文将介绍Bash中 set -euxo pipefail,它们可以帮助你写出更容易维护也更安全的脚本。这也是Bash脚本的终极调试手段,希望你以后在自己的脚本中加上这么一行,头顶也能少秃一点。

set -e

set -e 选项可以让你的脚本在出现异常时马上退出,后续命令不再执行。默认情况下Shell脚本不会因为错误而结束执行,但大多数情况是,我们希望出现异常时就不要再往下走了。假如你的if判断条件里会出现异常,这时脚本也会直接退出,但可能这并不是你期望的情况,这时你可以在判断语句后加上 || true 来阻止退出。

Before

#!/bin/bash

# 'foo' is a non-existing command
foo
echo "bar" # output
# ------
# line 4: foo: command not found
# bar

After

#!/bin/bash
set -e # 'foo' is a non-existing command
foo
echo "bar" # output
# ------
# line 5: foo: command not found

阻止立即退出的例子。

#!/bin/bash
set -e # 'foo' is a non-existing command
foo || true
echo "bar" # output
# ------
# line 5: foo: command not found
# bar

set -o pipefail

默认情况下Bash只会检查管道(pipeline)操作最后一个命令的返回值,假如最右边的命令成功那么它就认为这个语句没问题。这个行为其实是很不安全的,所以就有了set -o pipefail。这个特别的选项表示在管道连接的命令中,只要有任何一个命令失败(返回值非0),则整个管道操作被视为失败。只有管道中所有命令都成功执行了这个管道才算成功执行。

Before

#!/bin/bash
set -e # 'foo' is a non-existing command
foo | echo "a"
echo "bar" # output
# ------
# a
# line 5: foo: command not found
# bar

After

#!/bin/bash
set -eo pipefail # 'foo' is a non-existing command
foo | echo "a"
echo "bar" # output
# ------
# a
# line 5: foo: command not found

set -u

set -u 比较容易理解,Bash会把所有未定义的变量视为错误。默认情况下Bash会将未定义的变量视为空,不会报错,这也是很多坑的来源。也许由于变量名的细微差别让你查半天最后骂骂咧咧。

Before

#!/bin/bash
set -eo pipefail echo $a
echo "bar" # output
# ------
#
# bar

After

#!/bin/bash
set -euo pipefail echo $a
echo "bar" # output
# ------
# line 5: a: unbound variable

set -x

set -x 可以让Bash把每个命令在执行前先打印出来,你可以认为这就是Bash的Debug开关。它的好处当然显而易见,方便你快速找到有问题的脚本位置,但是也坏处也有吧,就是Bash的log会格外的乱。另外,它在打印命令前会把变量先解析出来,所以你可以知道当前执行的语句的变量值是什么。纵然log可能会乱一些,总比头发乱一些好,所以建议还是打开这个开关。

#!/bin/bash
set -euxo pipefail a=5
echo $a
echo "bar" # output
# ------
# + a=5
# + echo 5
# 5
# + echo bar
# bar

以上就是关于 set -euxo pipefail 的介绍,从Shell脚本的编写角度看,我十分建议所有人都应该在自己的Shell脚本里加上这么一行。但从实际情况看,如果你的Shell脚本已经超过200行,我更建议你换成高级语言来实现。比如Python或者Ruby甚至Perl,这些高级语言在Linux系统都是内置的,注意版本兼容性就好,写起来比Shell舒服太多了。

关于作者:

Toby Qin, Python 技术爱好者,目前从事测试开发相关工作,转载请注明原文出处。

欢迎关注我的博客 https://betacat.online,你可以到我的公众号中去当吃瓜群众。

Bash 脚本中的 set -euxo pipefail的更多相关文章

  1. grep 查找bash脚本中的注释代码

    出于安全性的考虑,不建议在bash脚本中注释掉不使用的代码.也就是说如果某段代码不使用了,那么应该删除掉,而不是简单地注释掉.假如你突然意识到这一点,而以前并没有遵从这个原则,现在需要找出脚本中的注释 ...

  2. 如何在Bash脚本中引入alias

    更多精彩内容,请关注微信公众号:后端技术小屋 alias的使用 在日常开发中,为了提高运维效率,我们会用alias(命令别名)来定义命令的简称.比如在~/.bash_profile中添加: alias ...

  3. bash 脚本中分号的作用

    在Linux bash shell中,语句中的分号一般用作代码块标识 1.单行语句一般要用到分号来区分代码块.比如: weblogic@pmtest:/$if [ "$PS1" ] ...

  4. Bash脚本中的操作符

    一.文件測试操作符 假设以下的条件成立将会返回真. -e 文件存在 -a 文件存在 这个选项的效果与-e同样. 可是它已经被"弃用"了, 而且不鼓舞使用. -f 表示这个文件是一个 ...

  5. 详解在bash脚本中如何获取自身路径

    DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 这是stac ...

  6. bash脚本中的普通数组和关联数组

    1. 普通数组 bash支持一维数组(不支持多维数组),并且没有限定数组的大小.类似与C语言,数组元素的下标由0开始编号.获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0. ...

  7. linux 环境下bash脚本中找不到命令

    mr.sh: line 1: HADOOP_CMD: command not found mr.sh: line 4: INPUT_FILE_PATH: command not found mr.sh ...

  8. bash脚本中使用选项 getopts

    原文链接 : http://note.youdao.com/noteshare?id=0cf08484c7308c763726e63e9a638ff5&sub=EF6A110E2F3345E6 ...

  9. Linux下shell脚本中信号捕获和函数练习脚本之ping一个网段

    该脚本主要的目的是练习在Linux bash脚本中捕获信号,顺便练习一下函数的使用,还有就是终止一个正在运行的程序后,该程序打开的文件的后续处理问题等等!脚本功能:  ping一个网段内的IP,检测哪 ...

随机推荐

  1. 洛谷训练新手村之“BOSS战-入门综合练习1”题解

    P1478 陶陶摘苹果(升级版) 题目链接:https://www.luogu.com.cn/problem/P1478 题目大意:陶陶有s点体力值,每个苹果消耗体力值,问s体力值最多能摘多少苹果. ...

  2. 吴恩达机器学习笔记 - cost function and gradient descent

    一.简介 cost fuction是用来判断机器预算值和实际值得误差,一般来说训练机器学习的目的就是希望将这个cost function减到最小.本文会介绍如何找到这个最小值. 二.线性回归的cost ...

  3. 【已解决】CentOS7使用yum安装Docker显示错误:cannot find a valid baseurl for repo: base/7/x86_64

    不得不说,Docker 要求 CentOS 系统的内核版本高于 3.10,这就让有些人开始头疼了,而要查看具体的版本可以用以下命令 uname -r 当然,CentOS 6.8版本也能安装Docker ...

  4. js获取url地址栏参数的方法,解决中文乱码问题,能支持中文参数

    第一种:参数只能是英文的 function getQuery(name) { var reg = new RegExp("(^|&)" + name + "=([ ...

  5. Maven 基础(一) | 使用 Maven 的正确姿势

    一.什么是 Maven? Maven 是一个项目管理工具,它的本质是一个项目对象模型(POM),体现在配置中就是我们常见的 pom.xml 文件,而这个 pom 文件就是 Maven 的核心,它管理了 ...

  6. Java 基础(四)| IO 流之使用文件流的正确姿势

    为跳槽面试做准备,今天开始进入 Java 基础的复习.希望基础不好的同学看完这篇文章,能掌握泛型,而基础好的同学权当复习,希望看完这篇文章能够起一点你的青涩记忆. 一.什么是 IO 流? 想象一个场景 ...

  7. maven常用的远程仓库地址

    <mirror> <id>nexus-aliyun</id> <name>Nexus aliyun</name> <url>ht ...

  8. Html中div块居中显示

    表面上这个问题很难,因为涉及到浏览器窗体大小,导致部分界面效果不一致.图中的方法适用于div块大小不变的界面. 如上所示,将其分为两块,margin-left和margin-top的值均分别为widt ...

  9. Nmap使用教程(一)

    基本扫描技术 扫描单个网络 nmap 192.168.1.1/www.baidu.com 扫描多个网络/目标 nmap 192.168.1.1 192.168.1.2 #将扫描同个网段内不同的ip地址 ...

  10. visul studio 使用git扫盲帖。

    写给和一样的菜B 有必要知道的命令: git rm --cached (文章底部有git命令大全) 创建.gitignore文件 windows版: 在项目根目录下面创建gitignore.txt文件 ...