IFS=$'\n'的确切含义是什么?

139
如果按照下面的示例,将IFS环境变量设置为换行符...
IFS=$'\n'
  • 美元符号到底代表什么?
  • 在这种情况下它有什么作用?
  • 在哪里可以阅读更多关于这个用法的信息(谷歌不允许搜索特殊字符,而我也不知道该搜些什么)?

我知道 IFS 环境变量的含义以及 \n 字符的含义(换行符),但为什么不直接使用以下形式: IFS="\n" (该形式不起作用)?

例如,如果我想循环遍历文件的每一行并使用 for 循环,我可以这样做:

for line in (< /path/to/file); do
    echo "Line: $line"
done

不过,除非将IFS设置为换行符,否则这种方法不会正确运行。为了使它正常工作,我需要这样做:

OLDIFS=$IFS
IFS=$'\n'
for line in (< /path/to/file); do
    echo "Line: $line"
done
IFS=$OLDIFS

注意: 我不需要另一种做同样事情的方法,我已经知道很多其他方法了...我只是对$'\n'感到好奇,并想知道是否有人能够解释一下。

7个回答

175

通常,bash不会在字符串字面值中解释转义序列。因此,如果您写\n"\n"'\n',那不是换行符-它是字母n(在第一种情况下)或后跟字母n的反斜杠(在其他两种情况下)。

$'somestring'是一种具有转义序列的字符串字面值语法。因此,与'\n'不同,$'\n'实际上是一个换行符。


2
并不完全正确——\n只是一个转义后的字母n。你是对的,'\n'"\n"都是反斜杠加上n。 - Roman Cheplyaka
17
请注意,$'\n' 是 Bash 特有的语法,在 POSIX shell (/bin/sh) 中不适用。为了在符合 POSIX 标准的方式下达到相同的效果,你可以输入 IFS=',然后按回车键来输入一个实际的换行符,接着再输入结束的 ' - Richard Hansen
25
IFS=$(echo -e '\n') 可以在符合 POSIX 的方式中实现。 - Vineet
14
@Vineet - 这让我停下来争辩一个被点赞的评论。虽然这是符合 POSIX 标准的,但它并不起作用——在 bash 中,命令替换运算符会删除所有尾随的换行符。请参见此链接了解更多详情。 - Digital Trauma
9
我认为这甚至不是POSIX:-e未定义,而\n没有-e也可作为XSI扩展使用。详见:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37 。printf '\n'很好用 ;) - Ciro Santilli OurBigBook.com
显示剩余4条评论

27
仅为了给这个结构以其官方名称:形如$'...'的字符串被称为ANSI C-quoted字符串,就像[ANSI] C中的字符串一样,将识别反斜杠转义序列并将其扩展为其文字等效项(有关支持的转义序列的完整列表,请参见下文)。此扩展后,$'...'字符串与'...'字符串的行为相同 - 即,它们被视为字面量而不再受任何[进一步的] shell扩展的影响。例如,$'\n'扩展为一个文字换行符,而常规的bash字符串文字(无论是'...'还是"...")都无法做到这一点。[1]另一个有趣的特征是,ANSI C-quoted字符串可以将' (单引号)作为\'进行转义,而常规单引号字符串'...'则无法进行该操作。
echo $'Honey, I\'m home' # OK; this cannot be done with '...'

支持的转义字符列表:

如果存在反斜杠转义字符,则将其解码如下:

\a 警报(响铃)

\b 退格

\e \E 转义字符(不是 ANSI C)

\f 换页符

\n 换行符

\r 回车符

\t 水平制表符

\v 垂直制表符

\ 反斜杠

\' 单引号

\" 双引号

\nnn 八位字符,其值为八进制值 nnn(一到三个数字)

\xHH 八位字符,其值为十六进制值 HH(一个或两个十六进制数字)

\uHHHH Unicode(ISO/IEC 10646)字符,其值为十六进制值 HHHH(一到四个十六进制数字)

\UHHHHHHHH Unicode(ISO/IEC 10646)字符,其值为十六进制值 HHHHHHHH(一到八个十六进制数字)

\cx 控制-x字符

扩展后的结果是单引号,就好像美元符号不存在一样。


[1] 但是,您可以在 '...' 和 "..." 字符串中嵌入实际换行符;即,您可以定义跨多行的字符串。


16

9

恢复默认的IFS-不需要使用OLDIFS=$IFS。在子shell中运行新IFS以避免覆盖默认的IFS:

ar=(123 321); ( IFS=$'\n'; echo ${ar[*]} )

除此之外,我并不完全相信你能完全恢复旧的IFS。你应该使用双引号来避免换行,例如OLDIFS="$IFS"

2
这是一个非常有用的技巧。我刚刚在清理shell连接操作中使用了它:args=$(IFS='&'; echo "$*")。以一种Bourne shell友好的方式恢复IFS$' \t\n'并不是一件容易的事情。 - jeberle
1
关于Besides I don't really believe you recover the old IFS fully:在变量赋值的右侧不执行单词拆分(但会删除引号),因此OLDIFS=$IFSOLDIFS="$IFS"的行为相同。 - mklement0

3

ANSI C引用字符串是一个关键点。感谢@mklement0。

您可以使用命令od测试ANSI C引用字符串。

echo -n $'\n' | od -c
echo -n '\n' | od -c
echo -n $"\n" | od -c
echo -n "\n" | od -c

输出:

0000000  \n  
0000001

0000000   \   n   
0000002

0000000   \   n   
0000002

0000000   \   n   
0000002

通过输出的内容,您可以清楚地了解其含义。


2

问题:

IFS=$'\n'的确切含义是什么?

简单回答:

嘿,Bash!将内部字段分隔符(IFS)设置为换行符


IFS是什么?

IFS是Bash在处理字符字符串时使用的单词/项边界字符。

它默认设置为空格、制表符和换行符等空白字符。

示例1:

使用IFS的默认值。

string="first second:third forth:fifth"

for item in $string; do
    echo "$item"
done

输出:

first
second:third
forth:fifth

例子2:

IFS 设置为 :

# Set the IFS to collon (:) character
IFS=:

string="first second:third forth:fifth"

for item in $string; do
    echo "$item"
done

输出:

first second  
third forth  
fifth

-9

这就像从一个变量中检索值:

VAR='test'
echo VAR
echo $VAR

这些符号的作用不同,所以美元符号基本上会评估内容。


6
这与变量无关。$'FOO'(不像本问题所涉及的$FOO)是一个字符串字面量。如果你执行echo $'VAR',你会看到它打印出字符串VAR,而不是test - sepp2k

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接