Bash 老司机也可能忽视的 10 大编程细节
Bash,作为大部分 Linux 发行版的出厂预设 Shell,因其晦涩难懂的语法设置,以及需要特别留心的编程细节,几乎成为 Linux 区别于其他操作系统的代名词。针对 Bash 中一些极容易出错的细节,我们在这里总结了 10 条编程注意事项,希望对各位泛 Linux 环境的开发者有所裨益。原文来自一位名叫 Julia Evans 的开发者博客,雷锋网编译。 作为一名 Bash 脚本编写经验超过 10 年的老程序员,我通常不用 Bash 处理复杂的编程任务。但作为一款我们在日常 Linux 使用中几乎无法避免的通用工具,Bash 的确有许多与我们习以为常的 C++ 和 Java 等高级语言非常不同的基础特性。在这里我并不打算讨论 Bash 编程的高阶应用,而是仅仅针对 Bash 中那些与众不同的基础特性做一简单梳理和汇总。希望对各位有所帮助。 当然,如果你对阅读博客不感兴趣,这里我再顺便推荐两个开源免费的小工具。一个是 Shell 语法检查工具 shellcheck,可以在运行前对脚本进行全面的语法检查;另一个是 shfmt,可以自动对写好的 Shell 脚本按照要求格式化。 shellcheck 地址:https://www.shellcheck.net/ shfmt 地址:https://github.com/mvdan/sh 1. 等号两边慎用空格Bash 中的赋值语句通常都是这样的:
然后我们通过 $VARIABLE 引用该变量。这里有一点非常重要,也极容易忽视的就是:千万不要在等号两边加空格。虽然加上空格也不会引起语法错误,但很可能造成意想不到的结果。例如 VARIABLE= 2 这个语句,解释器很可能会将一个空字符串赋值给 VARIABLE,然后运行一个名字叫 2 的脚本。 一般常用的 Bash 变量都是字符串,我很少见到有数组的。另外,虽然解释器也接受小写,但 Bash 中默认是将变量名全部大写的。 2. 用 ${} 限定变量名例如我定义了一个变量 MYVAR,内容是字符串“file.txt”,然后想执行如下命令: mv $MYVAR $MYVAR__bak # wrong! 结果一定会报错。因为解释器会搜索 MYVAR__bak 这个变量,而我们根本没有定义。因此,为了避免出现类似问题,最好的办法是每次引用时都在变量两边加上括号,就像这样: mv ${MYVAR} ${MYVAR}__bak # right! 3. 区分全局变量、局部变量和环境变量Bash 有三种变量:全局变量、局部变量和环境变量。其中最常用的是环境变量。 实际上每个 Linux 进程都有许多预设的环境变量(运行 env 命令可查看),Bash 中对环境的变量的应用非常简单。例如,想要查看 MYVAR 环境变量的值,可以运行下面这条命令: echo "$MYVAR" 想要设置环境变量,可以用这条命令: export MYVAR=2 需要注意的是,一旦在进程中设置了环境变量,那么这个环境变量会在所有与其相关的子进程中生效,例如下面这个例子: export MYVAR=2; python test.py $MYVAR 环境变量也会在 test.py 脚本中生效。 另一种是全局变量,如下所示这样的赋值语句实际上就是在定义全局变量: MYVAR=2 全局变量就像其他编程语言一样,会在整个代码中生效。 最后一种是局部变量,这种变量通常只在一个循环语句或者 Bash 函数中有效。一般不常用。 4. 活用命令替换通常我会用下面这段 for 循环打印输出 1-10 这 10 个数字。 for i in `seq 1 10` # you can use {1..10} instead of `seq 1 10` 如果把这些代码写到一行里,是这样的: for i in `seq 1 10`; do echo $i; done 这里我想强调的是,通过反引号(即键盘上Tab键上方的按键,注意不是单引号)将 seq 命令的输出结果,嵌入了 for 循环中直接使用。通过类似这种命令替换的方式,我们可以大大减少代码冗余,同时减少代码的出错几率。常见的替换方式有如下两种: OUTPUT=`command` 5. if 的注意事项if 语句的判定条件同时支持单中括号([])和双中括号([[]]),他们都可以用来隔离表达式和 if 关键词。但这里推荐使用双中括号,因为它的容错率更高,而且支持更多功能。另外,在 Linux 中单中括号 [ 实际与 test 命令是等价的,因此用双括号显然能避免更多的麻烦。 例如下面这段代码: If [[ -e /tmp/awesome.txt ]]; then 可以判断 awesome.txt 文件是否存在。 再比如下面的场景: $ [ 3 < 4 ] && echo "true" 使用单中括号会报错,但双中括号就没问题。 除了使用双中括号之外,还可以用 test 命令的运行结果作为 if 语句的判断条件,例如: test -e /tmp/awesome.txt 如果 awesome.txt 文件存在,则命令返回 0,否则返回错误码。 实际上,除了常见的 test 命令,所有返回固定数值的命令都可以作为 if 语句的判断条件。例如下面的代码: if grep peanuts food-list.txt |