有些开发人员使用Bash来实现非常复杂的功能,就像使用其他高级语言一样。他可能认为自己很棒,但其他人早就想粉碎他。Bash的可读性和可维护性远低于任何高级语言。更要命的是,Bash没有方便的调试工具和防错机制。如果出现问题,您必须排查很长时间。在Ruby或者Python等高级语言中,你可以很容易的知道错误是哪一行错误类型,还有IDE的Debugger加持。但是Bash只能看源码,通过打印日志等非常低效的方式调试。本文将介绍Bash中的set-euxopipefail,它可以帮助您编写更易于维护和更安全的脚本。这也是Bash脚本的终极调试方法。希望你以后能在自己的脚本中加入这样一行,脑袋就不会那么秃了。set-eset-e选项可以让你的脚本在发生异常时立即退出,并且不会执行后续命令。默认情况下,Shell脚本不会因为错误而结束执行,但在大多数情况下,我们希望当异常发生时,我们不会再继续下去。如果你的if判断条件出现异常,脚本也会直接退出,但这可能不是你所期望的。这时候可以加上||判断语句后为true,防止退出。Before#!/bin/bash#'foo'isanon-existingcommandfooecho"bar"#output#------#line4:foo:commandnotfound#barAfter#!/bin/bashset-e#'foo'是一个不存在的命令fooecho"bar"#output#------#line5:foo:commandnotfound防止立即退出的示例。#!/bin/bashset-e#'foo'是一个不存在的命令foo||trueecho"bar"#output#------#line5:foo:commandnotfound#barset-opipefailBash默认只检查管道操作最后一个命令的返回值,如果是最右边的命令成功然后它认为该语句可以。这种行为其实是很不安全的,所以就有了set-opipefail。这个特殊选项表示在流水线命令中,只要有一条命令失败(返回值不为0),就认为整个流水线操作失败。仅当管道中的所有命令都成功执行时,才认为管道成功。在#!/bin/bashset-e#'foo'之前是一个不存在的命令foo|echo"a"echo"bar"#output#------#a#line5:foo:commandnotfound#barAfter#!/bin/bashset-eopipefail#'foo'是一个不存在的命令foo|echo"a"echo"bar"#output#------#a#line5:foo:commandnotfoundset-uset-ucompare很容易理解Bash将所有未定义的变量都视为错误。默认情况下,Bash会将未定义的变量视为空,不会报错,这也是很多陷阱的来源。可能因为变量名的细微差别,你会查了半天,结果骂人。Before#!/bin/bashset-eopipefailecho$aecho"bar"#output#------##barAfter#!/bin/bashset-euopipefailecho$aecho"bar"#output#------#line5:a:unboundvariableset-xset-x可以让Bash在执行前打印出每条命令,你可以把这个看成是Bash的Debug开关。它的好处当然是显而易见的,方便你快速的找到有问题的脚本位置,但是也有坏处,就是Bash日志会异常的乱。另外,它会在打印命令之前解析变量,所以你可以知道当前正在执行的语句的变量值是多少。虽然日志可能比较乱,但总比头发乱,所以还是建议打开这个开关。#!/bin/bashset-euxopipefaila=5echo$aecho"bar"#output#------#+a=5#+echo5#5#+echobar#bar以上是set-euxopipefail简介,从编写shell脚本的角度,强烈建议大家在自己的shell脚本中加入这样一行。但是从实际情况来看,如果你的shell脚本超过200行,建议你换用高级语言来实现。比如Python或者Ruby甚至Perl,这些高级语言都是Linux系统内置的,只要注意版本兼容,写起来比Shell舒服多了。作者简介:TobyQin,Python技术爱好者,从事测试开发相关工作,转载请注明原文出处。欢迎关注我的博客https://tobyqin.cn,你可以去我的公众号做个吃瓜群众。
