【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 commandif-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)。

你可能感兴趣的:(【shell笔记>脚本】结构化命令之循环控制)