Parameter Details
IFS 内部字段分隔符
-x 执行命令时打印命令及其参数(Shell 选项)

Section 45.1: 内容、时间和原因?

当 shell 执行参数扩展、命令替换、变量或算术扩展时,它会扫描结果中的字边界。如果发现任何字边界,就会在该位置将结果分割成多个字。字边界由 shell 变量 IFS(内部字段分隔符)定义。IFS 的默认值是空格、制表符和换行符,也就是说,如果没有明确阻止,单词分割将在这三个空白字符上发生。

set -x
var='I am
a
multiline string'
fun() {
    echo "-$1-"
    echo "*$2*"
    echo ".$3."
}
fun $var

在上例中,fun 函数就是这样执行的:

fun I am a multiline string

将 $var 分割为 5 个参数,只打印 I、am 和 a。

Section 45.2: 分词的不良影响

$ a='I am a string with spaces'
$ [ $a = $a ] || echo "didn't match"
bash: [: too many arguments
didn't match

[ $a = $a ] 被解释为 [ I am a string with spaces = I am a string with spaces ] 。[ 是测试命令,其中 I am a string with spaces 不是一个参数,而是 6 个参数!"!

$ [ $a = something ] || echo "didn't match"
bash: [: too many arguments
didn't match

[ $a = something ] 被解释为[ I am a string with spaces = something ]

$ [ $(grep . file) = 'something' ]
bash: [: too many arguments

grep 命令返回的是一个带空格的多行字符串,所以你可以想象有多少个参数......

Section 45.3: 分词的作用

在某些情况下,分词很有用:

填充数组:

arr=($(grep -o '[0-9]\+' file))

这将用文件中找到的所有数值填满 arr

循环使用空格分隔的单词:

words='foo bar baz'
for w in $words;do
    echo "W: $w"
done

输出:

W: foo
W: bar
W: baz

传递不包含空格的空格分隔参数:

packs='apache2 php php-mbstring php-mysql'
sudo apt-get install $packs

或者

packs='
apache2
php
php-mbstring
php-mysql
'
sudo apt-get install $packs

这将安装软件包。如果用双引号包含 $packs,则会出错。

Unquoetd $packs将所有空格分隔的软件包名称作为参数发送给apt-get,而引用时会将$packs字符串作为单个参数发送,然后apt-get会尝试安装一个名为apache2 php php-mbstring php-mysql(第一个)的软件包,而这个软件包显然并不存在。

Section 45.4: 按分隔符分割的变化

我们可以简单地将分隔符从空格替换为新行,如下例所示。

echo $sentence | tr " " "\n"

它将分割变量句子的值,并逐行显示。

Section 45.5: 使用 IFS 进行拆分

为了更清楚起见,让我们创建一个名为 showarg 的脚本:

#!/usr/bin/env bash
printf "%d args:" $#
printf " <%s>" "$@"
echo

现在让我们看看它们的区别:

$ var="This is an example"
$ showarg $var
4 args: <This> <is> <an> <example>

$var 分成 4 个 args。IFS 是空格字符,因此单词分割发生在空格中

$ var="This/is/an/example"
$ showarg $var
1 args: <This/is/an/example>

在上文中,由于没有找到 IFS 字符,所以没有进行分词。

现在让我们设置 IFS=/

$ IFS=/
$ var="This/is/an/example"
$ showarg $var
4 args: <This> <is> <an> <example>

$var` 被分成 4 个参数,而不是一个参数。

Section 45.6: IFS 和分词

让我们将 IFS 设置为空格字符:

set -x
var='I am

a
multiline string'
IFS=' '
fun() {
    echo "-$1-"
    echo "*$2*"
    echo ".$3."
}
fun $var

这次分词只对空格有效。fun 函数将这样执行

fun I 'am
a
multiline' string

$var 分成 3 个参数。I, am\na\nmultiline 和字符串将被打印出来

让我们将 IFS 设置为仅换行:

IFS=$'\n'
...

现在,有趣的事情即将发生:

fun 'I am' a 'multiline string'

$var 分成 3 个参数。我是,将打印一个多行字符串

让我们看看将 IFS 设置为 nullstring 会发生什么:

IFS=
...

这一次,我们将这样来玩:

fun 'I am
a
multiline string'

$var 没有被拆分,即它仍然是一个 arg。

可以通过将 IFS 设置为 nullstring 来防止分词。

防止分词的一般方法是使用双引号:

fun "$var"

将在上述所有情况下防止分词,即执行 fun 函数时只有一个参数。