条件测试根据逻辑表达式的结果对Linux Bash脚本的执行流程进行分支。双括号条件测试大大简化了语法——但仍然有自己的问题。
单双括号
Bash 提供了test
命令。这使您可以测试逻辑表达式。该表达式将返回一个表示真或假响应的答案。返回值为零表示真实响应。零以外的任何值都表示错误。
与&&
操作员在命令行上链接命令使用此功能。仅当前一个命令成功完成时,才会执行命令。
如果测试为真,将打印“是”字样。
test 15 -eq 15 && echo "Yes"
test 14 -eq 15 && echo "Yes"
单括号条件测试模仿test
命令。它们将表达式括在方括号“ [ ]
”中并像test
命令一样操作。事实上,它们是相同的程序,由相同的源代码创建。唯一的操作区别是test
版本和[
版本如何处理帮助请求。
这是来自源代码:
/* Recognize --help or --version, but only when invoked in the "[" form, when the last argument is not "]". Use direct parsing, rather than parse_long_options, to avoid accepting abbreviations. POSIX allows "[ --help" and "[ --version" to have the usual GNU behavior, but it requires "test --help" and "test --version" to exit silently with status 0. */
我们可以通过询问看到这样的效果test
,并[
帮助和检查送往猛砸响应代码。
test --help
echo $?
[ --help
echo $?
双方test
并[
都外壳内建命令,意味着它们是置于猛砸。但也有一个独立的二进制版本的[
.
type test
type [
whereis [
相比之下,双括号条件测试[[
和]]
是关键字。[[
并]]
执行逻辑测试,但它们的语法不同。因为它们是关键字,所以您可以使用一些在单括号版本中无法使用的简洁功能。
Bash 支持双括号关键字,但并非在所有其他 shell中都可用。例如,Korn shell 确实支持它们,但普通的旧 shell sh 不支持。我们所有的脚本都以以下行开头:
#!/bin/bash
这确保我们调用 Bash shell 来运行脚本。
内置函数和关键字
我们可以使用该compgen
程序列出内置函数:
compgen -b | fmt -w 70
如果不通过管道输出输出,fmt
我们会得到一个很长的列表,每个内置函数都在自己的行上。在这种情况下,将内置函数组合成一个段落会更方便。
我们可以看到test
和[
在列表中,但]
没有列出。该[
命令查找关闭]
以检测何时到达表达式的末尾,但]
不是单独的内置命令。这只是我们提供的一个信号,[
用于指示参数列表的结尾。
要查看关键字,我们可以使用:
compgen -k | fmt -w 70
在[[
与]]
列表中的关键字都是,因为[[
是一个关键字,]]
是另一回事。它们是配对的,就像if
andfi
和case
and 一样esac
。
当 Bash 解析脚本或命令行并检测到具有匹配的关闭关键字的关键字时,它会收集它们之间出现的所有内容,并应用关键字支持的任何特殊处理。
使用内置命令,内建命令后面的内容就像传递给任何其他命令行程序的参数一样传递给它。这意味着脚本作者必须特别注意变量值中的空格等内容。
壳流
双括号条件测试可以使用 shell globbing。这意味着星号“ *
”将扩展为“任何东西”。
将以下文本键入或复制到编辑器中,并将其保存到名为“whelkie.sh”的文件中。
#!/bin/bash stringvar="Whelkie Brookes" if [[ "$stringvar" == *elk* ]]; then echo "Warning contains seafood" else echo "Free from molluscs" fi
为了使脚本可执行,我们需要使用带有 (execute) 选项的chmod
命令-x
。如果您想试用本文中的所有脚本,则需要对它们执行此操作。
chmod +x whelkie.sh
当我们运行脚本时,我们看到在字符串“Whelkie”中找到了字符串“elk”,不管它周围有什么其他字符。
./whelkie.sh
需要注意的一点是,我们不将搜索字符串用双引号括起来。如果您这样做,则不会发生通配符。搜索字符串将按字面处理。
允许使用其他形式的 shell globbing。问号“ ?
”将匹配单个字符,单个方括号用于表示字符范围。例如,如果您不知道使用哪种情况,您可以用一个范围覆盖这两种可能性。
#!/bin/bash stringvar="Jean-Claude van Clam" if [[ "$stringvar" == *[cC]lam* ]]; then echo "Warning contains seafood." else echo "Free from molluscs." fi
将此脚本另存为“damme.sh”并使其可执行。当我们运行它时,条件语句解析为真,并且执行 if 语句的第一个子句。
./damme.sh
引用字符串
我们之前提到过用双引号包裹字符串。如果这样做,就不会发生 shell globbing。尽管约定俗成说这是一种很好的做法,但在使用时不需要将字符串变量用引号括起来[[
,]]
即使它们包含空格。看下一个例子。the$stringvar
和$surname
string变量都包含空格,但条件语句中都没有引用。
#!/bin/bash stringvar="van Damme" surname="van Damme" if [[ $stringvar == $surname ]]; then echo "Surnames match." else echo "Surnames don't match." fi
将其保存到名为“surname.sh”的文件中并使其可执行。使用以下命令运行它:
./surname.sh
尽管两个字符串都包含空格,脚本还是会成功并且条件语句解析为 true。这在处理包含空格的路径和目录名称时很有用。在这里,-d
如果变量包含有效的目录名称,则该选项返回 true。
#!/bin/bash dir="/home/dave/Documents/Needs Work" if [[ -d ${dir} ]]; then echo "Directory confirmed" else echo "Directory not found" fi
如果您更改脚本中的路径以反映您自己计算机上的目录,将文本保存到名为“dir.sh”的文件中并使其可执行,您可以看到这是有效的。
./dir.sh
文件名通配问题
之间的有趣的差异[ ]
和[[ ]]
涉及到的文件名与他们通配符。格式“*.sh”将匹配所有脚本文件。[ ]
除非只有一个脚本文件,否则使用单括号会失败。查找多个脚本会引发错误。
这是带有单括号条件的脚本。
#!/bin/bash if [ -a *.sh ]; then echo "Found a script file" else echo "Didn't find a script file" fi
我们将此文本保存到“script.sh”中并使其可执行。我们检查了目录中有多少脚本,然后运行脚本。
ls
./script.sh
Bash 抛出错误。我们删除了除一个脚本文件之外的所有文件并再次运行该脚本。
ls
./script.sh
条件测试返回 true 并且脚本不会导致错误。编辑脚本以使用双括号提供了第三种行为。
#!/bin/bash if [[ -a *.sh ]]; then echo "Found a script file" else echo "Didn't find a script file" fi
我们将其保存到一个名为“dscript.sh”的文件中并使其可执行。在包含许多脚本的目录中运行此脚本不会引发错误,但该脚本无法识别任何脚本文件。
使用双括号的条件语句仅在不太可能的情况下解析为 true,即您在目录中有一个实际名为“*.sh”的文件。
./dscript.sh
逻辑与和或
双括号让您可以使用&&
and||
作为逻辑 AND 和 OR 运算符。
此脚本应将条件语句解析为 true,因为 10 等于 10,而25 小于 26。
#!/bin/bash first=10 second=25 if [[ first -eq 10 && second -lt 26 ]]; then echo "Condition met" else echo "Condition failed" fi
将此文本保存到名为“and.sh”的文件中,使其可执行,然后运行:
./and.sh
脚本按我们的预期执行。
这次我们将使用||
运算符。条件语句应该解析为真,因为尽管 10 不大于 15,但 25 仍然小于 26。只要第一次比较或第二次比较为真,整个条件语句就会解析为真。
将此文本另存为“or.sh”并使其可执行。
#!/bin/bash first=10 second=25 if [[ first -gt 15 || second -lt 26 ]]; then echo "Condition met." else echo "Condition failed." fi
./or.sh
正则表达式
双括号条件语句允许使用=~
运算符,它将字符串中的正则表达式搜索模式应用于语句的另一半。如果满足正则表达式,则认为条件语句为真。如果正则表达式没有找到匹配项,则条件语句将解析为 false。
将此文本保存到名为“regex.sh”的文件中,并使其可执行。
#!/bin/bash words="one two three" WordsandNumbers="one 1 two 2 three 3" email="dave@fabricateddomain.co.uk" mask1="[0-9]" mask2="[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}" if [[ $words =~ $mask1 ]]; then echo "\"$words\" contains digits." else echo "No digits found in \"$words\"." fi if [[ $WordsandNumbers =~ $mask1 ]]; then echo "\"$WordsandNumbers\" contains digits." else echo "No digits found in \"$WordsandNumbers\"." fi if [[ $email =~ $mask2 ]]; then echo "\"$email\" is a valid e-mail address." else echo "Couldn't parse \"$email\"." fi
第一组双括号使用字符串变量$mask1
作为正则表达式。这包含 0 到 9 范围内所有数字的模式。它将此正则表达式应用于$words
字符串变量。
第二组双括号再次使用字符串变量$mask1
作为正则表达式,但这次它与$WordsandNumbers
字符串变量一起使用。
最后一组双括号在字符串变量中使用了更复杂的正则表达式掩码$mask2
。
- [A-Za-z0-9._%+-]+:这匹配任何大写或小写字母,或从零到九的任何数字,或句点、下划线、百分号或加号或减号的任何字符. “
+
” 之外的“[]
” 表示对找到的尽可能多的字符重复这些匹配项。 - @:这仅匹配“@”字符。
- [A-Za-z0-9.-]+:这匹配任何大写或小写字母,或从零到九的任何数字,或句点或连字符的任何字符。“
+
” 之外的“[ ]
” 表示对找到的尽可能多的字符重复这些匹配项。 - . :这匹配“。” 仅字符。
- [A-Za-z]{2,4}:这匹配任何大写或小写字母。“
{2,4}
”表示至少匹配两个字符,最多匹配四个字符。
综上所述,正则表达式掩码将检查电子邮件地址的格式是否正确。
将脚本文本保存到名为“regex.sh”的文件中并使其可执行。当我们运行脚本时,我们得到了这个输出。
./regex.sh
第一个条件语句失败,因为正则表达式正在寻找数字,但$words
字符串变量中保存的值中没有数字。
第二个条件语句成功,因为$WordsandNumbers
字符串变量确实包含数字。
最后一个条件语句成功——也就是说,它解析为真——因为电子邮件地址的格式正确。
只有一个条件
双括号条件测试为您的脚本带来了灵活性和易读性。能够在条件测试中使用正则表达式证明学习如何使用[[
和]]
.
只需确保脚本调用支持它们的 shell,例如 Bash。