您需要在Linux中了解有关Bash for Loops的一切

Bash脚本是自动执行任务的高效方法,尤其是那些利用其他现有程序的任务。这种自动化通常需要多次重复类似的操作,而这恰恰是for循环产生作用的地方。

Linux和Mac系统管理员通常对通过终端进行脚本编写非常熟悉,但是即使Windows用户也可以通过Windows Linux子系统进行操作

Bash脚本如何工作

bash脚本只是一个纯文本文件,其中包含bash shell可以读取和执行的一系列命令。 Bash是Catalina之前的macOS和大多数Linux发行版中的默认外壳。

如果您以前从未使用过Shell脚本,则应从绝对最简单的情况开始。这将允许您练习关键概念,包括脚本的创建和执行。

首先,在方便的位置创建以下文件(理想情况下,打开终端并首先导航到所需目录):

 #!/bin/bash
echo "Hello, World"

第一行告诉运行该程序的程序如何运行(即使用bash解释器)。第二个只是与您在命令行中输入的其他命令相同的命令。将该文件另存为hello_world.sh ,然后:

 $ chmod +x hello_world.sh
$ ./hello_world.sh

第一行中的chmod命令使文件可执行,这意味着可以通过键入其名称来运行该文件,如第二行中所示。

如果您看到“ Hello,World”字样显示在终端的一行上,则说明一切正常。

循环如何工作

在一般编程中,有两种主要的for循环类型: numericforeach 。传统上,数字类型是最常见的类型,但在bash用法中,通常是相反的。

数值for循环通常集中在一个整数上,该整数确定将执行多少次迭代,例如:

 for (i = 0; i < 100; i++) {
/* statements to execute repeatedly */
}

这是一个看起来很熟悉的for循环,它将精确地重复100次,除非在循环中更改了i,否则另一条语句导致for循环的执行停止。

相比之下,Foreach循环倾向于对列表或数组之类的结构进行操作,并对该集合中的每个项目进行迭代:

 people = [ "Peter", "Paul", "Mary" ]
foreach (people as person) {
if (person == "Paul") {
...
}
}

一些语言使用略有不同的语法,该语法交换集合和项目的顺序:

 people = [ "Peter", "Paul", "Mary" ]
for (person in people) {
if (person == "Paul") {
...
}
}

对于循环

在bash中,foreach或for循环更常见。基本语法很简单:

 for arg in [list]
do
/* statements to execute repeatedly */
/* the value of arg can be obtained using $arg */
done

例如,要遍历三个显式命名的文件:

 for file in one.c two.c three.c
do
ls "$file"
done

如果当前目录中存在此类文件,则此脚本的输出为:

 one.c
two.c
three.c

可以通过全局模式(包括通配符-表示其他字符的特殊字符)来获取列表,而不是固定的文件集。在以下示例中,for循环遍历名称以“ .xml”结尾的所有文件(在当前目录中):

 for file in *.xml
do
ls -l "$file"
done

这是一些示例输出:

 $ -rw-r--r-- 1 bobby staff 2436 3 Nov 2019 feed.xml
$ -rw-r--r-- 1 bobby staff 6447 27 Oct 16:24 sitemap.xml

这看起来很像一个漫长的过程:

 $ ls -l *.xml

但是有一个显着的区别:for循环单独执行ls程序2次,每次都传递一个文件名。在单独的ls示例中,全局模式(* .xml)首先匹配文件名,然后将所有文件名作为单独的命令行参数发送到ls的一个实例。

这是一个使用wc (word count)程序使区别更加明显的示例:

 $ wc -l *.xml
44 feed.xml
231 sitemap.xml
275 total

wc程序分别计算每个文件中的行数,然后在所有文件中打印总计数。相反,如果wc在for循环内运行:

 for file in *.xml
do
wc -l $file
done

您仍然会看到每个文件的计数:

 44 feed.xml
231 sitemap.xml

但是没有总体摘要总数,因为每次循环迭代时, wc都是独立运行的。

当列表不是列表时

由于bash处理加引号的参数/字符串的方式,在处理for循环时有一个非常简单且常见的错误。循环浏览文件列表应如下所示:

 for file in one.c two.c

不是这样的:

 for file in "one.c two.c"

第二个示例将文件名括在双引号中,这导致列表中只有一个参数。 for循环只会执行一次。在以下情况下,可以通过使用变量来避免此问题:

 FILES="one.c two.c"
for file in $FILES
do
...
done

请注意,变量声明本身确实需要将其值括在双引号中!

对于没有列表

无需迭代,for循环可在调用脚本时对提供给脚本的命令行参数进行操作。例如,如果您有一个名为args.sh的脚本,其中包含以下内容:

 #!/bin/sh
for a
do
echo $a
done

然后执行args.sh将为您提供以下信息:

 $ ./args.sh one two three
one
two
three

Bash认识到这种情况,并将do视为等同于$ @ do中的do ,其中$ @是表示命令行参数的特殊变量。

模拟传统的数字For循环

Bash脚本通常处理文件列表或其他命令的输出行,因此for in循环类型很常见。但是,仍然支持传统的c样式操作:

 for (( i=1; i<=5; i++ ))
do
echo $i
done

这是经典形式,分为三个部分:

  1. 首次遇到循环时,将初始化变量(i = 1)
  2. 只要条件(i <= 5)为真,循环就会继续
  3. 每次循环时,变量都会递增(i ++)

在两个值之间进行迭代是一个足够普遍的要求,即存在一个更短,更不容易混淆的选择:

 for i in {1..5}
do
echo $i
done

大括号的扩展有效地将上述for循环转换为:

 for i in 1 2 3 4

具有中断和继续功能的更精细的循环控制

对于更复杂的循环,通常需要一种提前退出或立即使用下一个值重新启动主循环的方法。为此,bash借用了break和continue语句,这是其他编程语言中常见的。这是一个使用这两个示例查找长度超过100个字符的第一个文件的示例:

 #!/bin/bash
for file in *
do
if [ ! -f "$file" ]
then
echo "$file is not a file"
continue
fi
num_chars=$(wc -c < "$file")
echo $file is "$num_chars characters long"
if [ $num_chars -gt 100 ]
then
echo "Found $file"
break
fi
done

这里的for循环对当前目录中的所有文件进行操作。如果该文件不是常规文件(例如,如果是目录),则使用continue语句依次从下一个文件重新开始循环。如果是常规文件,第二个条件块将确定它是否包含100个以上的字符。如果是这样,则使用break语句立即退出for循环(并到达脚本的结尾)。

结论

bash脚本是一个包含一组可以执行的指令的文件。 for循环允许脚本的一部分重复多次。通过使用变量,外部命令以及break和继续语句,bash脚本可以应用更复杂的逻辑并执行各种各样的任务。