编写更紧凑的 Bash 代码
在任何编程语言中,都可以使用一些从阅读手册中可能不太明显的习惯用法。 通常,这些语言用法代表使您的代码更紧凑(即需要更少的代码行)的方法。 当然,有些人会避开这些习惯用法,认为它们代表不良风格。 当然,风格是在旁观者的眼中,本文并非旨在定义好或坏风格。 因此,对于那些可能想以风格为由进行评论的人,我将(重新)将您的注意力转移到/dev/null.
在大多数编程语言中,至少是非脚本语言中,您需要避免未初始化的变量。 在 bash 中,使用未初始化的变量通常可以简化您的代码。 例如,您可能首先想在 bash 中执行以下操作
$ cat code.bash
if test -f some_file; then
file_exists=1
else
file_exists=0
fi
if [[ $file_exists -eq 1 ]]; then
do_something
fi
但是,如果您记住在 bash 中引用未定义的变量不会导致错误,而只会返回一个空字符串,则可以将以上代码简化为以下内容
$ cat code.bash
if test -f some_file; then
file_exists=1
fi
if [[ $file_exists ]]; then
do_something
fi
而不是检查变量file_exists是否具有特定值(0 或 1),您只需检查它是否具有值即可。
我在编写本文时学到的一件事是,我错误地认为第二个 if 语句中的条件中的变量需要用双引号引起来(即[[ "$file_exists" ]]),否则在未设置变量的情况下,bash 会产生错误; 似乎并非如此。 我不确定这是否在某个时候发生了变化,或者我是否一直都错了。
继续这个例子,你可以做的另一件事是使用&&和||(与 和 或) 运算符来简化您的 if 语句。
&&和||运算符用于在 bash 中分隔命令,它们是短路运算符,这意味着如果执行左侧足以知道总体退出状态,则它们不会执行右侧的命令。
例如,在 A 与 B 中,如果 A 为 false,则无需评估 B,因为只有两者都为 true 时,整个表达式才能为 true;类似地,在 A 或 B 中,如果 A 为 true,则无需评估 B,因为只需要一个为 true 表达式即为 true。 在处理 bash 时,为真通常可以解释为命令 以零退出状态终止,而 为假意味着命令 以非零退出状态终止。 现在您可以将上面的代码简化为
$ cat code.bash
test -f some_file && file_exists=1
[[ $file_exists ]] && do_something
用命令列表替换 if 语句,命令列表用&&和||分隔,在循环中也可以方便地跳出循环或继续到下一个迭代
$ cat code.bash
for i in *.pdf
do
test $i == '*.pdf' && break
test ${i:0:1} == 'a' || continue
done
第一个test上面检查循环变量是否等于循环中的 glob 表达式(如果没有匹配项,则会发生这种情况),如果是,则跳出循环。 第二个test检查匹配文件的名称是否以字母 a 开头,如果不是,则跳到循环的下一次迭代(我们只执行continue如果比较失败)。 显然,在这个人为的例子中,你可以使用a*.pdf在 for 循环中的 glob 表达式中。
当使用&&和||运算符来替换简单的 if 语句时,你可能会陷入的一个陷阱是,你可能认为以下内容有效
$ cat code.bash
test -f some_file && file_exists=1; echo 'File exists'
但它不起作用,因为分号的优先级低于&&和||,因此它的执行方式就像你写的一样
$ cat code.bash
if test -f some_file; then
file_exists=1
fi
echo 'File exists'
要使其工作,您需要在&&后面的代码放在花括号中{}:
$ cat code.bash
test -f some_file && { file_exists=1; echo 'File exists'; }
请注意,大括号周围应该有空格,并且列表末尾的分号是强制性的(请参阅手册页以获取更多解释)。