bash 脚本中 IFS=$'\n' 的含义是什么?

Abd*_*red 230 command-line bash shell-script

bash shell 脚本的开头是以下行:

IFS=$'\n'
Run Code Online (Sandbox Code Playgroud)

这组符号背后的含义是什么?

Tbl*_*lue 273

IFS代表“内部字段分隔符”。shell 使用它来确定如何进行分词,即如何识别单词边界。

在像 bash 这样的 shell 中试试这个(其他 shell 可能会以不同的方式处理这个,例如 zsh):

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done
Run Code Online (Sandbox Code Playgroud)

的默认值IFS由空白字符组成(准确地说:空格、制表符和换行符)。每个字符可以是一个词边界。因此,使用默认值IFS,上面的循环将打印:

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done
Run Code Online (Sandbox Code Playgroud)

换句话说,shell 认为空格是单词边界。

现在,IFS=:在执行循环之前尝试设置。这次的结果是:

Word: foo:bar
Word: baz
Word: rab
Run Code Online (Sandbox Code Playgroud)

现在,shell 也拆分mystring为单词——但现在,它只将冒号视为单词边界。

的第一个字符IFS是特殊的:它用于在使用特殊$*变量时分隔输出中的单词(示例取自Advanced Bash Scripting Guide,您还可以在其中找到有关此类特殊变量的更多信息):

$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z
Run Code Online (Sandbox Code Playgroud)

相比于:

$ bash -c 'set w x y z; IFS="-:;"; echo "$*"'
w-x-y-z
Run Code Online (Sandbox Code Playgroud)

请注意,在这两个示例中,shell 仍会将所有字符:,-;视为单词边界。唯一改变的是 的行为$*

另一个需要了解的重要事情是如何处理所谓的“IFS 空白” 。基本上,只要IFS包含空格字符,前导和尾随空格就会在处理之前从要拆分的字符串中剥离,并且一系列连续的空格字符也会分隔字段。但是,这仅适用于实际存在于IFS.

例如,让我们看看在字符串"a:b:: c d "(尾随空间和之间有两个空格字符cd)。

  1. 随着IFS=:将它分为四个领域:"a""b"""(空字符串)和" c d "(再次,两者之间的空间cd)。请注意最后一个字段中的前导和尾随空格。
  2. 使用IFS=' :',它将被拆分为五个字段:"a""b"""(空字符串)"c""d"。任何地方都没有前导和尾随空格。

请注意在第二个示例中多个连续的空格字符如何分隔两个字段,而多个连续的冒号则不然(因为它们不是空格字符)。

至于IFS=$'\n',这是一个ksh93语法也通过支持bashzshmksh和FreeBSD sh(所有壳之间有变化)。引用 bash 联机帮助页:

$'string' 形式的词被特殊处理。该词扩展为“字符串”,并按照 ANSI C 标准的规定替换反斜杠转义字符。

\n是换行符的转义序列,因此IFS最终被设置为单个换行符。

  • 不那么相关,但如果你很好奇,你可能想看看 `unset IFS` 如何使 shell 的行为与 `IFS=` 非常不同。此外,IFS 中的第一个字节对于 `"${named_array[*]}"` 也是特殊的 - 但当扩展没有被引用时,这两个都不重要。 (7认同)
  • 这很好,但是,在我看来,您最好阅读和理解 POSIX 规范,而不是“bash”脚本指南或其他内容。总的来说,此类链接中可用的信息在重要方面缺乏。无论如何,因此错过了有关 shell 拆分的两个关键点 - globbing 和 IFS 空白。 (5认同)
  • 3- `$IFS` 也被 `read` 内置命令使用 (5认同)
  • @szx `"\n"`(或`'\n'`)与`$'\n'` 不同。第一个字符串由字符 _backslash_ 和 `n` 组成。`$'\n'` 产生一个换行符。为了获得最佳的可移植性,您不能使用 `$'\n'`,因为它只被某些 shell 支持。将 IFS 设置为换行符的一种方法是 `IFS=$(printf '\n.'); IFS=${IFS%.}`(命令扩展删除了尾随换行符,所以我们添加然后删除另一个尾随字符来解​​决这个问题)。 (3认同)

cho*_*oba 29

在带美元的单引号内,一些字符被特别评估。例如,\n被转换为新行。

因此,此特定行将换行符分配给变量 IFS。反过来,IFS 是 bash 中的一个特殊变量:内部字段分隔符。正如man bash所说,它

用于扩展后的分词,并使用read内置命令将行拆分为单词。默认值为<space><tab><newline>

  • +1 用于提及与简单单引号不同的“美元单引号”。 (10认同)
  • @Snowcrash +1 表示 *+1 表示提及 **dollared 单引号**,这与简单的单引号 * 不同。抱歉,忍不住 :) 但实际上,指出这一点非常好,因为它很重要! (4认同)
  • @Pryftan +1 for +1 for +1 ......你知道......这真的很重要。 (2认同)

cuo*_*glm 21

简而言之,IFS=$'\n'将换行符分配\n给变量IFS

$'string'构造是一种引用机制,用于像转义序列一样解码 ANSI C。这种语法来自ksh93,并且可以移植到现代 shell 中,例如bash, zsh, pdksh, busybox sh

此语法不是由 POSIX 定义的,但已被SUS issue 7接受。