Content #
test命令和测试表达式使用标准的数学比较符号来表示字符串比较,而用文本代码来表示数值比较。
这个细微的特性被很多程序员理解反了。如果你对数值使用了数学运算符号,那么shell会将它们当成字符串值,并可能产生错误结果。
From #
Linux命令行与shell脚本编程大全
case命令会采用列表格式来检查变量的多个值:
case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac
case命令会将指定变量与不同模式进行比较。如果变量与模式匹配,那么shell 就会执行为该模式指定的命令。你可以通过竖线运算符在一行中分隔出多个模式。星号会捕获所有与已知模式不匹配的值。
$ cat ShortCase.sh
#!/bin/bash
# Using a short case statement
#
case $USER in
rich | christine)
echo "Welcome $USER"
echo "Please enjoy your visit.";;
barbara | tim)
echo "Hi there, $USER"
echo "We're glad you could join us.";;
testing)
echo "Please log out when done with test.";;
*)
echo "Sorry, you are not allowed here."
esac
$
$ ./ShortCase.sh
Welcome christine
Please enjoy your visit.
$
case命令提供了一种更清晰的方法来为变量每个可能的值指定不同的处理选择。
...双方括号命令提供了针对字符串比较的高级特性。双方括号的格式如下:
[ [ expressio ] ]
expression可以使用test命令中的标准字符串比较。除此之外,它还提供了test 命令所不具备的另一个特性——模式匹配。
在进行模式匹配时,可以定义通配符或正则表达式来匹配字符串:
$ cat DoubleBrackets.sh
#!/bin/bash
# Using double brackets for pattern matching
if [[ $BASH_VERSION == 5.* ]]
then
echo "You are using the Bash Shell version 5 series."
fi
$
$ ./DoubleBrackets.sh
You are using the Bash Shell version 5 series.
$
上述脚本中使用了双等号(==)。双等号会将右侧的字符串(5.*)视为一个模式并应用模式匹配规则。双方括号命令会对$BASH_VERSION环境变量进行匹配,看是否以字符串5.起始。如果是,则测试通过,shell会执行then部分的命令。
Linux命令行与shell脚本编程大全
双括号命令允许在比较过程中使用高级数学表达式。双括号命令的格式如下:
(( expression ))
expression可以是任意的数学赋值或比较表达式。双括号命令既可以在if语句中使用,也可以在脚本中的普通命令里用来赋值:
$ cat DoubleParentheses.sh
#!/bin/bash
# Testing a double parentheses command
val1=10
if (( $val1 ** 2 > 90 ))
then
(( val2 = $val1 ** 2 ))
echo "The square of $val1 is $val2,"
echo "which is greater than 90."
fi
$ ./DoubleParentheses.sh
The square of 10 is 100,
which is greater than 90.
$
注意,双括号中表达式的大于号不用转义。这是双括号命令又一个优越性的体现。
Linux命令行与shell脚本编程大全
单括号允许在if语句中使用子shell(子shell的用法参见第5章)。单括号形式的test命令格式如下:
(command)
在bash shell执行command之前,会先创建一个子shell,然后在其中执行命令。如果命令成功结束,则退出状态码会被设为0,then部分的命令就会被执行。如果命令的退出状态码不为0,则不执行then部分的命令。
$ cat SingleParentheses.sh
#!/bin/bash
# Testing a single parentheses condition
echo $BASH_SUBSHELL
if (echo $BASH_SUBSHELL)
then
echo "The subshell command operated successfully."
else
echo "The subshell command was NOT successful."
fi
$
$ ./SingleParentheses.sh
01
The subshell command operated successfully.
$
当脚本第一次(在if语句之前)执行echo $BASH_SUBSHELL命令时,是在当前 shell中完成的。该命令会输出0,表明没有使用子shell。在if语句内,脚本在子shell中执行echo $BASH_SUBSHELL命令,该命令会输出1,表明使用了子shell。子shell操作成功结束,接下来是执行then部分的命令。
Linux命令行与shell脚本编程大全
无须为每个需要创建的新用户账户手动输入useradd命令,可以将需要添加的新用户账户放在一个文本文件中,然后创建一个简单的脚本来进行处理。这个文本文件的格式如下:
loginname, name
第一项是为新用户账户所选用的用户id。第二项是用户的全名。两个值之间以逗号分隔。
将IFS分隔符设置成逗号,并将其作为while语句的条件测试部分。然后使用read 命令读取文件中的各行。实现代码如下所示:
while IFS=',' read -r userid name
read命令会自动移往CSV文本文件的下一行,因此就无须专门再写一个循环了。当read命令返回假值的时候(也就是读取完整个文件),while命令就会退出。
要想把数据从文件中传入while命令,只需在while命令尾部使用一个重定向符即可。
$ cat test26
#!/bin/bash
# process new user accounts
input="users.csv"
while IFS=',' read -r loginname name
do
echo "adding $loginname"
useradd -c "$name" -m $loginname
done < "$input"
$
$input变量中保存的是数据文件名,该数据文件被作为while命令的数据源。 users.csv文件内容如下:
$ cat users.csv
rich,Richard Blum
christine,Christine Bresnahan
barbara,Barbara Blum
tim,Timothy Bresnahan
$
Linux命令行与shell脚本编程大全
while命令允许在while语句行定义多个测试命令。只有最后一个测试命令的退出状态码会被用于决定是否结束循环。
$ cat test11
#!/bin/bash
# testing a multicommand while loop
var1=10
while echo $var1
[ $var1 -ge 0 ]
do
echo "This is inside the loop"
var1=$[ $var1 - 1 ]
done
$ ./test11
10
This is inside the loop
9
This is inside the loop
8
This is inside the loop
7
This is inside the loop
6
This is inside the loop
5
This is inside the loop
4
This is inside the loop
3
This is inside the loop
2
This is inside the loop
1
This is inside the loop
0
This is inside the loop
-1
$
在while语句中定义了两个测试命令:
...无须使用文件进行重定向,只需在命令行中指定用于输入重定向的数据即可。
内联输入重定向运算符是双小于号(<<)。除了这个符号,必须指定一个文本标记来划分输入数据的起止。任何字符串都可以作为文本标记,但在数据开始和结尾的文本标记必须一致:
command << marker
data
marker
在命令行中使用内联输入重定向时,shell会用PS2环境变量中定义的次提示符来提示输入数据,其用法如下所示:
$ wc << EOF
> test string 1
> test string 2
> test string 3
> EOF
3 9 42
$
次提示符会持续显示,以获取更多的输入数据,直到输入了作为文本标记的那个字符串。wc命令会统计内联输入重定向提供的数据包含的行数、单词数和字节数。
Linux命令行与shell脚本编程大全
$ cat test4
#!/bin/bash
# using a variable to hold the list
list="Alabama Alaska Arizona Arkansas Colorado"
list=$list" Connecticut"
for state in $list
do
echo "Have you ever visited $state?"
done
$ ./test4
Have you ever visited Alabama?
Have you ever visited Alaska?
Have you ever visited Arizona?
Have you ever visited Arkansas?
Have you ever visited Colorado?
Have you ever visited Connecticut?
$
$list变量包含了用于迭代的值列表。
...在处理代码量较大的脚本时,可能在一个地方需要修改IFS的值,然后再将其恢复原状,而脚本的其他地方则继续沿用IFS的默认值。一种安全的做法是在修改 IFS之前保存原来的IFS值,之后再恢复它。
IFS.OLD=$IFS
IFS=$'\n'
<在代码中使用新的IFS值>
IFS=$IFS.OLD
如果要遍历文件中以冒号分隔的值(比如/etc/passwd文件),则只需将IFS的值设为冒号即可:
IFS=:
如果要指定多个IFS字符,则只需在赋值语句中将这些字符写在一起即可:
IFS=$'\n:;"'
该语句会将换行符、冒号、分号和双引号作为字段分隔符。如何使用IFS字符解析数据没有任何限制。
典型的例子是处理/etc/passwd文件。这要求你逐行遍历该文件,将IFS变量的值改成冒号,以便分隔开每行中的各个字段。
#!/bin/bash
# changing the IFS value
IFS.OLD=$IFS
IFS=$'\n'
for entry in $(cat /etc/passwd)
do
echo "Values in $entry -"
IFS=:
for value in $entry
do
echo " $value"
done
done
这个脚本使用了两个不同的IFS值来解析数据。第一个IFS值解析出/etc/passwd 文件中的各行。内层for循环接着将IFS的值修改为冒号,以便解析出 /etc/passwd文件各行中的字段。
Linux命令行与shell脚本编程大全