1.14 字段分隔符和迭代器

内部字段分隔符(Internal Field Separator,IFS)是shell脚本中的一个重要概念。在处理文本数据时,它可是相当有用。我们将会讨论把单个数据流划分成不同数据元素的定界符。内部字段分隔符是用于特定用途的定界符。IFS是存储定界符的环境变量。它是当前shell环境使用的默认定界字符串。

考虑一种情形:我们需要迭代一个字符串或CSV(Comma Separated Value,逗号分隔型数值)中的单词。在前者中,我们使用IFS=".";在后者中,则使用IFS=","。让我们看看应该怎么做。

1.14.1 预备知识

考虑CSV数据的情况:

  1. data="name,sex,rollno,location"
  2. #我们可以使用IFS读取变量中的每一个条目
  3. oldIFS=$IFS
  4. IFS=, #将IFS设置为逗号
  5. for item in $data;
  6. do
  7. echo Item: $item
  8. done
  9. IFS=$oldIFS

输出如下:

  1. Item: name
  2. Item: sex
  3. Item: rollno
  4. Item: location

IFS的默认值为空白字符(换行符、制表符或者空格)。

当IFS被设置为逗号时,shell将逗号解释成一个定界符,因此变量 $item在每次迭代中读取由逗号分隔的字串作为变量值。

如果没有把IFS设置成",",那么上面的脚本会将全部数据作为单个字符串打印出来。

1.14.2 实战演练

让我们以 /etc/passwd为例,看看IFS的另一种用法。在文件 /etc/passwd中,每一行包含了由冒号划分的多个条目。文件中的每行都对应一位用户的相关属性。

考虑这样的输入:root:x:0:0:root:/root:/bin/bash。每行的最后一项指定了用户的默认shell。可以按照下面的方法巧妙地利用IFS打印出用户以及他们默认的shell:

  1. #!/bin/bash
  2. #用途: 演示IFS的用法
  3. line="root:x:0:0:root:/root:/bin/bash"
  4. oldIFS=$IFS;
  5. IFS=":"
  6. count=0
  7. for item in $line;
  8. do
  9. [ $count -eq 0 ] && user=$item;
  10. [ $count -eq 6 ] && shell=$item;
  11. let count++
  12. done;
  13. IFS=$oldIFS
  14. echo $user\'s shell is $shell;

输出为:

  1. root's shell is /bin/bash

对一系列值进行迭代的时候,循环非常有用。Bash提供了多种类型的循环。下面就来看看怎么样使用它们。

for循环

  1. for var in list;
  2. do
  3. commands; # 使用变量$var
  4. done
  5. list can be a string, or a sequence.

我们可以轻松地生成不同的序列。

echo {1..50}能够生成一个从1到50的数字列表。

echo {a..z}{A..Z},或是使用{a..h}生成部分列表。类似地,将这些方法结合起来,我们就可以连接(concatenate)数据。

下面的代码中,变量i在每次迭代的过程里都会保存一个字符,范围从az

  1. for i in {a..z}; do actions; done;

for循环也可以采用C语言中for循环的格式。例如:

  1. for((i=0;i<10;i++))
  2. {
  3. commands; # 使用变量$i
  4. }

while循环

  1. while condition
  2. do
  3. commands;
  4. done

true作为循环条件能够产生无限循环。

until循环

在Bash中还可以使用一个特殊的循环until。它会一直执行循环直到给定的条件为真。例如:

  1. x=0;
  2. until [ $x -eq 9 ]; # [ $x -eq 9 ] is the condition
  3. do let x++; echo $x;
  4. done