【shell笔记>脚本】结构化命令之循环控制
内容
- for循环语句
- until迭代语句使用while语句
- 循环
- 重定向循环的输出
这一节我们来了解如何重复一些过程和命令,也就是循环执行一组命令直到达到了某个特定条件。
for命令
基本格式:
for var in list
do
commands
done
也可以
for var in list; do
分号只用来分隔命令的,让代码更简约。
来个简单例子:
wsx@wsx-ubuntu:~/script_learn$ cat test1
#!/bin/bash
# basic for command
for test in Alabama Alaska Arizona Arkansas California Colorado
do
echo The next state is $test
done
wsx@wsx-ubuntu:~/script_learn$ ./test1
The next state is Alabama
The next state is Alaska
The next state is Arizona
The next state is Arkansas
The next state is California
The next state is Colorado
这里操作基本和其他语言一致(格式不同),不多讲啦。
在读取列表中的复杂值时,我们可能会遇到问题。比如下面这个例子:
wsx@wsx-ubuntu:~/script_learn$ cat badtest1
#!/bin/bash
# another example of how not to use the for command
for test in I don't know if this'll work
do
echo "word:$test"
done
wsx@wsx-ubuntu:~/script_learn$ ./badtest1
word:I
word:dont know if thisll
word:work
我们可以看到shell看到了列表值中的单引号尝试使用它们来定义一个单独的数据值。
这里有两种解决办法:
- 使用转义字符将单引号转义
- 使用双引号来定义用到单引号的值
我们将这两种解决办法同时用到上个例子:
wsx@wsx-ubuntu:~/script_learn$ cat test2
#! /bin/bash
# another example of how not to use the for command
for test in I don\'t know if "this'll" work; do
echo "word:$test"
done
wsx@wsx-ubuntu:~/script_learn$ ./test2
word:I
word:don't
word:know
word:if
word:this'll
word:work
我们可能明白了for
循环是假定每个值是用空格分隔的,所以当有包含空格的数据时,我们需要用双引号括起来。
通常我们会将列表值存储在一个变量中,然后通过遍历变量的方式遍历了其内容的的列表。
看看怎么完成这个任务:
wsx@wsx-ubuntu:~/script_learn$ cat test3
#!/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
wsx@wsx-ubuntu:~/script_learn$ ./test3
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
变量包含的已有列表中添加了一个值。这是在已有文本字符串尾部添加文本的一种常用方法。
我们还可以用命令来输出我们需要的列表内容:
wsx@wsx-ubuntu:~/script_learn$ cat test4
#!/bin/bash
# reading values from a file
file="states"
for state in $(cat $file)
do
echo "Visit beautiful $state"
done
wsx@wsx-ubuntu:~/script_learn$ cat states
Alabama
Alaska
Arizona
Arkansas
Colorado
Connecticut
Delaware
Florida
Georgia
wsx@wsx-ubuntu:~/script_learn$ ./test4
Visit beautiful Alabama
Visit beautiful Alaska
Visit beautiful Arizona
Visit beautiful Arkansas
Visit beautiful Colorado
Visit beautiful Connecticut
Visit beautiful Delaware
Visit beautiful Florida
Visit beautiful Georgia
更改字段分隔符
环境变量IFS
,也叫作字段分隔符。它定义了bash shell用作字段分隔符的一系列字符。默认情况下,bash shell会将空格、制表符和换行符当作字段分隔符。
如果想修改IFS
的值,比如使其只能识别换行符,我们可以将下面这行代码加入脚本:
IFS=$'\n'
在处理大量脚本时,我们可能只在某一部分使用其他的分隔符,这时候可以先保存原有的IFS
值,然后修改,最后恢复:
IFS.OLD=$IFS
IFS=$'\n'
<在代码中使用新的IFS值>
IFS=$IFS.OLD
假如我们要遍历一个文件中用冒号分隔的值:
IFS=:
假如要指定多个IFS
字符,只要将它们的赋值行串起来:
IFS=$'\n':;"
这个赋值会将换行符、冒号、分号以及双引号作为字段分隔符。
用通配符读取目录
我们可以用for
命令来自动遍历目录中的文件。进行此操作时,必须在文件名或路径名中使用通配符。它会强制shell使用文件扩展匹配。文件扩展匹配是生成匹配指定通配符的文件名或路径名的过程。
我拿我的一个目录来尝试一下:
wsx@wsx-ubuntu:~/script_learn$ cat test5
#!/bin/bash
# iterate through all the files in a directory
for file in /home/wsx/python_learn/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
fi
done
wsx@wsx-ubuntu:~/script_learn$ ./test5
/home/wsx/python_learn/athletelist.py is a file
/home/wsx/python_learn/athletemodel.py is a file
/home/wsx/python_learn/ch2_data_input.py is a file
/home/wsx/python_learn/chapter5_first.py is a file
/home/wsx/python_learn/chapter6_first.py is a file
/home/wsx/python_learn/chapter6_second.py is a file
/home/wsx/python_learn/chapter6_third.py is a file
/home/wsx/python_learn/coinFlips.py is a file
/home/wsx/python_learn/Dive_into_python is a directory
注意:第一个方括号之后和第二个方括号之前必须加上一个空格,否则会报错。
在Linux中,目录名和文件名中包含空格是合法的,所以将$file
变量用双引号圈起来。当然,大家尽量不要让文件或目录包含空格,不然很容易出问题(命令会把空格当做文件的分隔符)。
C语言风格的for命令
C语言风格的for
命令看起来如下:
for (( a = 1; a < 10; a++ ))
值得注意的是,这里有些部分没有遵循bash shell标准的for
命令:
- 变量赋值可以有空格;
- 条件中的变量不以美元符开头;
- 迭代过程的算式未用
expr
命令格式。
在使用这种格式时要小心,不同的格式不注意就会出错。
下面举个例子:
wsx@wsx-ubuntu:~/script_learn$ cat test6
#!/bin/bash
# testing the C-style for loop
for (( i=1; i <= 10; i++ ))
do
echo "The next number is $i"
done
wsx@wsx-ubuntu:~/script_learn$ ./test6
The next number is 1
The next number is 2
The next number is 3
The next number is 4
The next number is 5
The next number is 6
The next number is 7
The next number is 8
The next number is 9
The next number is 10
while命令
while
命令的格式为:
while test command
do
other commands
done
while
命令某种意义上是if-then
语句和for
循环的混杂体。注意,这里while
后面接的也是命令。while
命令允许定义一个要测试的命令,然后循环执行一组命令,只要定义的测试命令返回的是退出状态码是0(类似一般语言中 的TRUE)。直到非0时退出循环。
while
命令中定义的test command
和if-then
语句中的格式一模一样。可以使用任何普通的bash shell命令,或者用test
命令进行条件测试,比如测试变量值。
最常见的用法是用方括号来检查循环命令中用到的shell
变量的值。
wangsx@SC-201708020022:~/tmp$ cat test
#/bin/bash
# while command test
var1=10
while [ $var1 -gt 0 ]
do
echo $var1
var1=$[ $var1 - 1 ]
done
wangsx@SC-201708020022:~/tmp$ ./test
10
9
8
7
6
5
4
3
2
1
使用多个测试命令
while
命令允许我们在while
语句行中定义多个测试命令。只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环。
比如while echo $var1 [ $var1 -ge 0 ]
检测的就是后面方括号命令的退出状态码。
until命令
until
命令和while
命令工作的方式完全相反。只有测试命令的退出状态码不为0,bash shell才会执行循环中列出的命令。一旦测试命令返回了退出状态码0,循环就结束了。
until test command
do
other commands
done
一个例子:
wangsx@SC-201708020022:~/tmp$ cat test12
#!/bin/bash
# using the until command
var1=100
until [ $var1 -eq 0 ]
do
echo $var1
var1=$[ $var1 - 25 ]
done
wangsx@SC-201708020022:~/tmp$ ./test12
100
75
50
25
同样地,在until
命令中放入多个测试命令时也要注意(类似while
)。