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等等,其中c是IFS*中的第一个字符集。