Contents

IFS在Bash脚本中的含义

1. 概述

在本教程中,我们将了解特殊的 shell 变量 Internal Field Separator ( IFS )。首先,我们将讨论IFS变量的默认值并了解它的各种用例。然后,我们将通过在IFS中设置自定义值来探索一些实现。

2. IFS变量及其默认值

特殊的 shell 变量IFS确定 Bash 在拆分字符串序列时如何识别单词边界。** IFS的默认值是由空格、制表符和换行符组成的三字符字符串**:

$ echo "$IFS" | cat -et
 ^I$
$

这里我们使用cat 命令的*-e-t选项来显示IFS*变量的特殊字符值。

现在,让我们运行一个示例来演示对空格分隔的字符串进行分词:

$ string="foo bar foobar"
$ for i in $string
> do
>  echo "'$i' is the substring"
> done
'foo' is the substring 
'bar' is the substring
'foobar' is the substring

当我们遍历 字符串时,我们可以获得单独的子字符串,因为空格是 IFS 变量的默认值之一。

如果输入包含制表符分隔值,我们将观察到类似于上面的输出:

 for i in `echo -e "foo\tbar\tfoobar"`; do echo "'$i' is the substring"; done

除了空格和制表符之外,IFS变量还将换行符作为默认值。让我们在换行符分隔的输入上验证字段拆分效果:

$ string="foo
> bar
> foobar"
$ printf "%q\n" "$string"
$'foo\nbar\nfoobar'
$ for i in $string
> do
> echo "'$i' fetched from the string"
> done
'foo' fetched from the string
'bar' fetched from the string
'foobar' fetched from the string

在这里,我们可以从多行输入中拆分换行符分隔的字段。

3. 在IFS中设置自定义值

在上一节中,我们了解了IFS变量的默认值。此外,我们还可以在IFS变量中设置自定义值。这有助于我们处理各种以字段分隔的字符串。

让我们通过一个例子来学习如何做到这一点:

$ string=foo:bar:foobar
$ old_ifs="$IFS"
$ IFS=":"
$ for i in $string
> do
>   echo "'$i' is the splitted word"
> done
'foo' is the splitted word
'bar' is the splitted word
'foobar' is the splitted word

在此示例中,在更新 IFS 变量的值之前,我们将其值存储在另一个变量old_ifs中。

因此,我们可以稍后使用此变量将IFS重置为其原始值:

IFS="$old_ifs"

我们还可以取消设置 IFS以将 Bash 行为重置为其默认值:

$ echo "$IFS" | cat -et
 ^I$
$
$ string="foo bar foo:bar"
$ for i in $string; do echo "[$i] extracted"; done
[foo] extracted
[bar] extracted
[foo:bar] extracted
$ IFS=":"  && echo "$IFS" | cat -et
:$
$ for i in $string; do echo "[$i] extracted"; done
[foo bar foo] extracted
[bar] extracted
$ unset IFS  && echo "$IFS" | cat -et
$
$ for i in $string; do echo "[$i] extracted"; done
[foo] extracted
[bar] extracted
[foo:bar] extracted

请注意,在我们取消设置 IFS之后,Bash 会处理分词,就好像IFS保持默认值一样。

但是,如果我们将IFS设置为空字符串,那么 Bash 将不会执行任何拆分操作:

$ IFS=""
$ for i in $string; do echo "[$i] extracted"; done
[foo bar foo:bar] extracted

3.1. 默认IFS与自定义IFS

当 IFS 保存默认空白字符值与保存自定义值时, IFS行为存在细微差别。

IFS设置为默认值时,将从输入字符串中去除前导和尾随空白字符。与此连续空白字符序列一起被视为单个分隔符。但是,当在IFS变量中设置自定义值时,不会观察到此行为。

让我们用一个例子来验证默认的IFS行为:

string="foo  bar    foobar     "
for i in $string
do
  echo "[$i] extracted"
done
[foo] extracted
[bar] extracted
[foobar] extracted

这里的输入字符串由尾随空格和单词之间的多个空格组成。但是,Bash 忽略了尾随空白字符,并将单词之间的多个空格视为单个分隔符。

让我们通过在IFS变量中设置自定义值(冒号)来检查另一个示例:

$ string="foo:bar::foobar::"
$ IFS=":"
$ for i in $string
> do
>   echo "[$i] extracted"
> done
[foo] extracted
[bar] extracted
[] extracted
[foobar] extracted
[] extracted

在这种情况下,输入字符串的单词之间的多个冒号不会被视为单个分隔符。

3.2. 使用read命令的用例

让我们使用我们关于解析 CSV的文章 中的一个示例来了解使用read 命令的IFS变量的用例:

#! /bin/bash
while IFS="," read -r rec_column1 rec_column2 rec_column3 rec_column4
do
 echo "Displaying Record-$rec_column1"
 echo "Quantity: $rec_column2"
 echo "Price: $rec_column3"
 echo "Value: $rec_column4"
 echo ""
done < <(tail -n +2 input.csv)

值得注意的是,我们在while循环中将**IFS设置为*“,”* ,以将input.csv的每一行分解为标记。因此,我们可以使用read命令将逗号分隔的字段值解析为 Bash 变量。

3.3. IFS和位置参数

特殊的命令行参数 $@和$* 保存传递给 Bash 脚本或函数的参数列表(位置参数)。

让我们创建一个示例脚本:

#!/bin/bash
echo " Input Parameters using \$@ : $@"
echo " Input Parameters using \$* : $*"

现在,让我们使用一些位置参数执行脚本:

$ ./inputs.sh arg1 arg2 arg3
 Input Parameters using $@ : arg1 arg2 arg3
 Input Parameters using $* : arg1 arg2 arg3

正如我们所见,$@和$*都以类似的方式保存脚本的输入参数。我们现在将更新我们的脚本以将IFS变量设置为自定义值:

#!/bin/bash
IFS='|'
echo " Input Parameters using \$@ : $@"
echo " Input Parameters using \$* : $*"

让我们观察执行输出:

$ ./inputs.sh arg1 arg2 arg3
 Input Parameters using $@ : arg1 arg2 arg3
 Input Parameters using $* : arg1|arg2|arg3

这里的区别是输入参数之间的连接。$IFS在使用$*时充当位置参数的分隔符。参数扩展为*$1c$2等等,其中cIFS*中的第一个字符集。