??xml version="1.0" encoding="utf-8" standalone="yes"?> test、[、[[?(、和 if-then-else 解密 U别Q?中 Ian Shields (ishields@us.ibm.com), 高E序? IBM 2007 q?3 ?16 ?/p>
Bash shell 在当今的许多 Linux® ?UNIX® pȝ上都可用,?Linux 上常见的默认 shell。Bash 包含强大的编E功能,其中包括丰富的可试文gcd和属性的函数Q以及在多数~程语言中可以用的术和字W串比较函数。理解不同的试q认识到 shell q能把一些操作符解释?shell 元字W,是成为高U?shell 用户的重要一步。这文章摘?developerWorks 教程 LPI exam 102 prep: Shells, scripting, programming, and compilingQ介l了如何理解和?Bash shell 的测试和比较操作?/p>
q个技巧解释了 shell 试和比较函敎ͼ演示了如何向 shell d~程功能。?zhn)可能已经看到q?&& ?|| 操作W的?shell 逻辑Q它允许(zhn)根据前一条命令的退出状态(正确退出或伴随错误退出)而执行后一条命令。在q个技巧中Q将看到如何把这些基本的技术扩展成更复杂的 shell ~程?/p>
在Q何一U编E语a中,学习(fn)了如何给变量分配值和传递参C后,都需要测试这些值和参数。在 shell 中,试?x)设|返回的状态,q与其他命o(h)执行的功能相同。实际上Q?code>test 是个内置命o(h)Q?/p>
内置命o(h) 在清?1 的第一个示例中Q?code>-gt 操作W对两个字符g间执行算术比较。在W二个示例中Q用 可以? 可以分别用操作符 说明Q?/strong>shell 也用 ?1 昄了一些更常见的文件测试。如果被试的文件存在,而且有指定的特征Q则l果?True?/p>
除了上面的单目测试,q可以用表 2 所C的双目操作W比较两个文Ӟ(x) 其他一些测试可以用来测试文件许可之cȝ内容。请参阅 bash 手册获得更多l节或? 最后, 同? 在? 甚至q可以在 虽然使用以上的测试和 bash ? Bash 中的 使用q今为止学到的东西,现在能够构徏单的计算器来计算术表达式,如清?9 所C:(x) q个计算器利? 如清?9 所C,需要确保在表达式?shell 元字W?—?例如(???gt; ?< ?—?正确地对表达式{义。无论如何,现在有了一个非常方便的计器Q可以像 shell 那样q行术计算?/p>
在清?9 中可能注意到 对输入进行的额外处理出了本技巧的范围Q所以请心使用q个计算器?/p>
当然Q也可以只用
Linux 技? Bash 试和比较函?/h1>
(zhn)是否ؓ(f) Bash shell 中大量的试和比较选项而困惑呢Q这个技巧可以帮助?zhn)解密不同cd的文件、算术和字符串测试,q样(zhn)就能够知道什么时候?
test
?[ ]
?[[ ]]
?(( ))
?if-then-else
了?/blockquote>
test
Ҏ(gu)表达?em>expr 求值的l果q回 0Q真Q或 1Q假Q。也可以使用Ҏ(gu)P(x)test expr
?[ expr ] 是等L(fng)?可以?$?
查返回|可以使用 && ?|| 操作q回|也可以用本技巧后面介l的各种条gl构试q回倹{?/p>
清单 1. 一些简单测?/strong>
[ian@pinguino ~]$ test 3 -gt 4 && echo True || echo false
false
[ian@pinguino ~]$ [ "abc" != "def" ];echo $?
0
[ian@pinguino ~]$ test -d "$HOME" ;echo $?
0
[ ]
的Ş式比较两个字W串不相{。在最后一个示例中Q测?HOME 变量的|用单目操作符 -d
查它是不是目录?/p>
-eq
?-ne
?code>-lt?-le
?-gt
?-ge
比较术|它们分别表示{于、不{于、小于、小于等于、大于、大于等于?=
?!=
?code>< ?>
比较字符串是否相{、不相等或者第一个字W串的排序在W二个字W串的前面或后面。单目操作符 -z
试 null 字符Ԍ如果字符串非I?-n
q回 TrueQ或者根本没有操作符Q?/p>
<
?>
操作W进行重定向Q所以必ȝ \<
?\>
加以转义。清?2 昄了字W串试的更多示例。检查它们是否如(zhn)预期的一栗?/p>
清单 2. 一些字W串试
[ian@pinguino ~]$ test "abc" = "def" ;echo $?
1
[ian@pinguino ~]$ [ "abc" != "def" ];echo $?
0
[ian@pinguino ~]$ [ "abc" \< "def" ];echo $?
0
[ian@pinguino ~]$ [ "abc" \> "def" ];echo $?
1
[ian@pinguino ~]$ [ "abc" \<"abc" ];echo $?
1
[ian@pinguino ~]$ [ "abc" \> "abc" ];echo $?
1
操作W?/th>
特征
-d
目录
-e
存在Q也可以?-aQ?/td>
-f
普通文?/td>
-h
W号q接Q也可以?-LQ?/td>
-p
命名道
-r
可读
-s
非空
-S
套接?/td>
-w
可写
-N
从上ơ读取之后已l做q修?/td>
操作W?/th>
?True 的情?
-nt
试 file1 是否?file2 更新。修Ҏ(gu)期将用于q次和下ơ比较?/td>
-ot
试 file1 是否?file2 旧?/td>
-ef
试 file1 是不?file2 的硬链接?
help test
查看内置试的简要信息。也可以?help
命o(h)了解其他内置命o(h)?/p>
-o
操作W允许测试利?set -o 选项
讄的各U?shell 选项Q如果设|了该选项Q则q回 True (0)Q否则返?False (1)Q如清单 3 所C?/p>
清单 3. 试 shell 选项
[ian@pinguino ~]$ set +o nounset
[ian@pinguino ~]$ [ -o nounset ];echo $?
1
[ian@pinguino ~]$ set -u
[ian@pinguino ~]$ test -o nounset; echo $?
0
-a
?-o
选项允许使用逻辑q算W?AND ?OR 表辑ּl合在一赗单目操作符 !
可以使测试的意义相反。可以用括号把表辑ּ分组Q覆盖默认的优先U。请C shell 通常要在?shell 中运行括号中的表辑ּQ所以需要用 \( ?\) 转义括号Q或者把q些操作W括在单引号或双引号内。清?4 演示了摩Ҏ(gu)则在表达式上的应用?/p>
清单 4. l合和分l测?/strong>
[ian@pinguino ~]$ test "a" != "$HOME" -a 3 -ge 4 ; echo $?
1
[ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o 3 -lt 4 \) ]; echo $?
1
[ian@pinguino ~]$ [ ! \( "a" = "$HOME" -o '(' 3 -lt 4 ')' ")" ]; echo $?
1
test
命o(h)非常强大Q但是很难满_转义需求以及字W串和算术比较之间的区别。幸q的是,bash 提供了其他两U测试方式,q两U方式对熟?zhn)?C、C++ ?Java® 语法的h来说?x)更自然些?/p>
(( ))
复合命o(h) 计算术表达式,如果表达式求gؓ(f) 0Q则讄退出状态ؓ(f) 1Q如果求gؓ(f)?0 |则设|ؓ(f) 0。不需要对 ((
?))
之间的操作符转义。算术只Ҏ(gu)数进行。除 0 ?x)生错误,但不会(x)生溢出。可以执?C 语言中常见的术、逻辑和位操作?let
命o(h)也能执行一个或多个术表达式。它通常用来为算术变量分配倹{?/p>
清单 5. 分配和测试算术表辑ּ
[ian@pinguino ~]$ let x=2 y=2**3 z=y*3;echo $? $x $y $z
0 2 8 24
[ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w
0 3 8 16
[ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w
0 4 8 13
(( ))
一P利用复合命o(h) [[ ]]
可以Ҏ(gu)件名和字W串使用更自然的语法。可以用括号和逻辑操作W把 test
命o(h)支持的测试组合v来?
清单 6. 使用 [[ 复合命o(h)
[ian@pinguino ~]$ [[ ( -d "$HOME" ) && ( -w "$HOME" ) ]] &&
> echo "home is a writable directory"
home is a writable directory
=
?!=
操作W时Q复合命?[[
q能在字W串上进行模式匹配。匹配的方式像清单 7 所C的通配W匹配?/p>
清单 7. ?[[ q行通配W测?/strong>
[ian@pinguino ~]$ [[ "abc def .d,x--" == a[abc]*\ ?d* ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def c" == a[abc]*\ ?d* ]]; echo $?
1
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* ]]; echo $?
1
[[
复合命o(h)内执行算术测试,但是千万要小心。除非在 ((
复合命o(h)内,否则 <
?>
操作W会(x)把操作数当成字符串比较ƈ在当前排序序列中试它们的顺序。清?8 用一些示例演CZq一炏V?/p>
清单 8. ?[[ 包含术试
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || (( 3 > 2 )) ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 -gt 2 ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 > 2 ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a > 2 ]]; echo $?
0
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a -gt 2 ]]; echo $?
-bash: a: unbound variable
&&
?||
控制操作W能实现许多~程Q但 bash q包含了更熟(zhn)的 “if, then, else” ?case l构。学?fn)完q些之后Q将学习(fn)循环l构Q这h的工L(fng)真正得到扩展?/p>
if
命o(h)是个复合命o(h)Q它试一个测试或命o(h)Q?code>$?Q的q回|q根据返回gؓ(f) TrueQ?Q或 FalseQ不?0Q进行分支。虽然上面的试只返?0 ?1 |但命令可能返回其他倹{请参阅 LPI exam 102 prep: Shells, scripting, programming, and compiling 教程学习(fn)q方面的更多内容?/p>
if
命o(h)有一?then
子句Q子句中包含试或命令返?0 时要执行的命令列表,可以有一个或多个可选的 elif
子句Q每个子句可执行附加的测试和一?then
子句Q子句中又带有相关的命o(h)列表Q最后是可选的 else
子句及命令列表,在前面的试?elif
子句中的所有测试都不ؓ(f)真的时候执行,最后?fi
标记表示该结构结束?
清单 9. ?if、then、else 计算表达?/strong>
[ian@pinguino ~]$ function mycalc ()
> {
> local x
> if [ $# -lt 1 ]; then
> echo "This function evaluates arithmetic for you if you give it some"
> elif (( $* )); then
> let x="$*"
> echo "$* = $x"
> else
> echo "$* = 0 or is not an arithmetic expression"
> fi
> }
[ian@pinguino ~]$ mycalc 3 + 4
3 + 4 = 7
[ian@pinguino ~]$ mycalc 3 + 4**3
3 + 4**3 = 67
[ian@pinguino ~]$ mycalc 3 + (4**3 /2)
-bash: syntax error near unexpected token `('
[ian@pinguino ~]$ mycalc 3 + "(4**3 /2)"
3 + (4**3 /2) = 35
[ian@pinguino ~]$ mycalc xyz
xyz = 0 or is not an arithmetic expression
[ian@pinguino ~]$ mycalc xyz + 3 + "(4**3 /2)" + abc
xyz + 3 + (4**3 /2) + abc = 35
local
语句?x 声明为局部变量,只能?mycalc
函数的范围内使用?code>let 函数h几个可用的选项Q可以执行与它密切关联的 declare
函数。请参?bash 手册或?help let
获得更多信息?/p>
else
子句和最后的两个CZ。可以看刎ͼ?xyz
传递给 mycalc q没有错误,但计结果ؓ(f) 0。这个函数还不够灵yQ不能区分最后用的CZ中的字符|所以不能警告用戗可以用字W串模式匚w试Q例?[[ ! ("$*" == *[a-zA-Z]* ]]
Q或使用适合自己范围的Ş式)消除包含字母表字W的表达式,但是q会(x)妨碍在输入中使用 16 q制标记Q因Z?16 q制标记时可能要?0x0f 表示 15。实际上Qshell 允许的基数最高ؓ(f) 64Q?base#value
标记Q,所以可以在输入中加?_ ?@ 合法C用Q何字母表字符? q制?16 q制使用常用的标记方式,开头ؓ(f) 0 表示八进Ӟ开头ؓ(f) 0x ?0X 表示 16 q制。清?10 昄了一些示例?/p>
清单 10. 用不同的基数q行计算
[ian@pinguino ~]$ mycalc 015
015 = 13
[ian@pinguino ~]$ mycalc 0xff
0xff = 255
[ian@pinguino ~]$ mycalc 29#37
29#37 = 94
[ian@pinguino ~]$ mycalc 64#1az
64#1az = 4771
[ian@pinguino ~]$ mycalc 64#1azA
64#1azA = 305380
[ian@pinguino ~]$ mycalc 64#1azA_@
64#1azA_@ = 1250840574
[ian@pinguino ~]$ mycalc 64#1az*64**3 + 64#A_@
64#1az*64**3 + 64#A_@ = 1250840574
elif
语句非常方便。它允许化羃q,从而有助于脚本~写。在清单 11 中可能会(x)?type
命o(h)?mycalc
函数中的输出感到惊讶?/p>
清单 11. Type mycalc
[ian@pinguino ~]$ type mycalc
mycalc is a function
mycalc ()
{
local x;
if [ $# -lt 1 ]; then
echo "This function evaluates arithmetic for you if you give it some";
else
if (( $* )); then
let x="$*";
echo "$* = $x";
else
echo "$* = 0 or is not an arithmetic expression";
fi;
fi
}
$(( 表达?/em> ))
?echo
命o(h)q行 shell 术q算Q如清单 12 所C。这样就不必学习(fn)关于函数或测试的M内容Q但是请注意 shell 不会(x)解释元字W,例如 *Q因此元字符不能?(( 表达?/em> ))
?[[ 表达?/em> ]]
中那h常发挥作用?
清单 12. ?shell 中用 echo ?$(( )) 直接q行计算
[ian@pinguino ~]$ echo $((3 + (4**3 /2)))
35
]]>
|上有一些制作Makfile的文章,只停留在Makefile而已。用autotools的工L(fng)Ҏ(gu)说要单的多,其它一些介lautotools文章又有很多漏洞Q而且步骤烦琐?br />
制作一个最单的helloworldE序Q?br />
现有目录test
mkdir src 建立src目录存放 源代?br />在src下?br />~辑hello.c文g