为什么在对C风格字符串的内容进行循环时,bash会忽略换行符?

EMP*_*tor 5 bash scripting for-loop escaping

为什么以下......

c=0; for i in $'1\n2\n3\n4'; do echo iteration $c :$i:; c=$[c+1]; done
Run Code Online (Sandbox Code Playgroud)

打印...

iteration 0 :1 2 3 4:
Run Code Online (Sandbox Code Playgroud)

并不是

iteration 0 :1:
iteration 1 :2:
iteration 2 :3:
iteration 3 :4:
Run Code Online (Sandbox Code Playgroud)

根据我的理解,$'STRING'语法应该允许我指定一个带转义字符的字符串.不应该将"\n"解释为换行符,以便for循环回声四次,每行一次?相反,似乎新行被解释为空格字符.

我采取了放松的建议并尝试设置$ IFS.结果是一样的.

IFS=$'\n'; c=0; for i in $'1\n2\n3\n4'; do echo iteration $c :$i:; c=$[c+1]; done; unset IFS;

iteration 0 :1 2 3 4:
Run Code Online (Sandbox Code Playgroud)

William Purssel在一篇评论中表示这不起作用,因为IFS被设置为换行符...但是后续行动无效.

IFS=' '; c=0; for i in '1 2 3 4'; do echo iteration $c :$i:; c=$[c+1]; done; unset IFS;

iteration 0 :1 2 3 4:
Run Code Online (Sandbox Code Playgroud)

在换行符分隔的字符串上使用IFS =''导致更加混乱......

IFS=' '; c=0; for i in $'1\n2\n3\n4'; do echo iteration $c :$i:; c=$[c+1]; done; unset IFS;

iteration 0 :1
2
3
4:
Run Code Online (Sandbox Code Playgroud)

将IFS设置为'\n'而不是$'\n'与IFS =''具有相同的效果...

IFS='\n'; c=0; for i in $'1\n2\n3\n4'; do echo iteration $c :$i:; c=$[c+1]; done; unset IFS;

iteration 0 :1
2
3
4:
Run Code Online (Sandbox Code Playgroud)

只有一次迭代,但由于某种原因,换行在回声中可见.

首先将字符串存储在变量中,然后循环遍历变量的内容(无需设置IFS):

c=0; v=$'1\n2\n3\n4'; for i in $v; do echo iteration $c :$i:; c=$[c+1]; done

iteration 0 :1:
iteration 1 :2:
iteration 2 :3:
iteration 3 :4:
Run Code Online (Sandbox Code Playgroud)

这仍然无法解释为什么会出现这个问题.

这里有模式吗?这是unwind链接中定义的IFS的预期行为吗?

unwind的链接状态..."shell扫描参数扩展,命令替换和算术扩展的结果,这些结果在双引号内没有出现,用于分词."

我猜这就解释了为什么无论使用什么转义字符,字符串文字都不会被分割为for-loop迭代.只有当文字被分配给变量时,该变量才会被展开以便为for循环进行拆分它是否有效.我想还有命令替换.

例子:

命令替换的结果是分开的

c=0; for i in `echo $'1\n2\n3\n4'`; do echo iteration $c :$i:; c=$[c+1]; done

iteration 0 :1:
iteration 1 :2:
iteration 2 :3:
iteration 3 :4:
Run Code Online (Sandbox Code Playgroud)

扩展的字符串部分被拆分,休息不是.

c=0; v=$'1 \n\t2\t3 4'; for i in $v$'\n5\n6'; do echo iteration $c :$i:; c=$[c+1]; done

iteration 0 :1:
iteration 1 :2:
iteration 2 :3:
iteration 3 :4 5 6:
Run Code Online (Sandbox Code Playgroud)

当扩展发生在双引号中时,不会发生分裂.

c=0; v=$'1\n2\n3 4'; for i in "$v"; do echo iteration $c :$i:; c=$[c+1]; done

iteration 0 :1 2 3 4:
Run Code Online (Sandbox Code Playgroud)

任何SPACE,TAB,NEWLINE序列都用作分割的分隔符.

c=0; v=$'1 2\t3 \t\n4'; for i in $v; do echo iteration $c :$i:; c=$[c+1]; done

iteration 0 :1:
iteration 1 :2:
iteration 2 :3:
iteration 3 :4:
Run Code Online (Sandbox Code Playgroud)

我将接受放松的答案,因为他的链接产生了我的问题的答案.

没有任何关于为什么for循环中的回声行为随IFS值变化的线索.

编辑:扩展澄清.

Pau*_*ce. 7

在这种情况下,Bash不对引用的字符串进行单词扩展.例如:

$ for i in "a b c d"; do echo $i; done
a b c d

$ for i in a b c d; do echo $i; done
a
b
c
d

$ var="a b c d"; for i in "$var"; do echo $i; done
a b c d

$ var="a b c d"; for i in $var; do echo $i; done
a
b
c
d
Run Code Online (Sandbox Code Playgroud)

在评论中,你说"IFS ='\n'也有效.什么不起作用是IFS = $'\n'.我现在非常困惑."

IFS='\n',您将分隔符(复数)设置为两个字符反斜杠和"n".所以,如果你这样做(在"\n"的中间插入一个"X"),你会看到会发生什么.尽管你将它们包括在内,但它仍然按字母顺序处理"\n"序列$'':

$ IFS='\n'; for i in $'a\Xnb\nc\n'; do echo $i; done; rrifs
a X b
c
Run Code Online (Sandbox Code Playgroud)

编辑2(响应评论):

它看作'\n'两个字符(不是换行符)和$'a\Xnb\nc\n'10个字符的文字字符串(没有换行符)然后echo输出字符串并将"\n"序列解释为换行符(因为字符串被"标记"用于解释),但是它被引用它被视为一个字符串而不是由分隔的单词$IFS.

试试这些进一步比较:

$ c=0; for i in "a\nb\nc\n"; do echo -e "iteration $c :$i:"; c=$[c+1]; done
iteration 0 :a
b
c
:

$ c=0; for i in "a\nb\nc\n"; do echo "iteration $c :$i:"; c=$[c+1]; done
iteration 0 :a\nb\nc\n:

$ c=0; for i in a\\nb\\nc\\n; do echo -e "iteration $c :$i:"; c=$[c+1]; done
iteration 0 :a
b
c
:

$ c=0; for i in a\\nb\\nc\\n; do echo "iteration $c :$i:"; c=$[c+1]; done
iteration 0 :a\nb\nc\n:
Run Code Online (Sandbox Code Playgroud)

设置IFS对上面没有影响.

这有效(注意$varfor声明中没有引用):

$ var=$'a\nb\nc\n'
$ saveIFS="$IFS"   # it's important to save and restore $IFS
$ IFS=$'\n'        # set $IFS to a newline using $'\n' (not '\n')
$ c=0; for i in $var; do echo -e "iteration $c :$i:"; c=$[c+1]; done
iteration 0 :a:
iteration 1 :b:
iteration 2 :c:
$ IFS="$saveIFS"
Run Code Online (Sandbox Code Playgroud)


unw*_*ind 6

更改您的$IFS设置以更改bash将文本拆分为单词的方式.

编者注:
这个答案被接受了,因为它提供了一个信息链接,最终解释了潜在的问题.
但请注意,OP的问题不能简单地通过更改来解决$IFS,因为$IFS不适用于引用的字符串.

  • 问题是IFS包含换行符.试试IFS ='' (2认同)