5

根据手册页中的“read -N”描述:

-N nchars 仅在准确读取 NCHARS 字符后返回,除非遇到 EOF 或读取超时,忽略任何分隔符

但是,在回答以下命令时:

$ echo 'a b' | while read -N1 c; do echo ">>>$c<<<"; done
>>>a<<<
>>><<<
>>>b<<<
>>><<<

空格和换行符都被翻译成空字符串,而在命令中:

$ echo 'a b' | while IFS= read -N1 c; do echo ">>>$c<<<"; done
>>>a<<<
>>> <<<
>>>b<<<
>>>
<<<

空格和换行符已正确存储在变量中。

因此,分隔符在“read”或“while”命令中似乎仍有一些处理,我不明白。

我们可以将这些结果与使用“read -n”的结果进行比较,该手册描述为:

-n nchars 在读取 NCHARS 个字符后返回,而不是等待换行符,但如果在分隔符之前读取的 NCHARS 个字符少于此,则使用分隔符

$ echo 'a b' | while read -n1 c; do echo ">>>$c<<<"; done
>>>a<<<
>>><<<
>>>b<<<
>>><<<

$ echo 'a b' | while IFS= read -n1 c; do echo ">>>$c<<<"; done
>>>a<<<
>>> <<<
>>>b<<<
>>><<<
4

4 回答 4

4

这是POSIX行为。分配给变量时,应剥离 IFS 字符: 结果应拆分为参数扩展结果的 shell 中的字段(当然,-n 和 -N 不是 POSIX)。

这是由read源代码注释产生的:

/* This code implements the Posix.2 spec for splitting the words
     read and assigning them to variables. */
  orig_input_string = input_string;

  /* Remove IFS white space at the beginning of the input string.  If
     $IFS is null, no field splitting is performed. */
于 2015-08-23T15:41:43.520 回答
3

在我看来,在使用 option-N时,的行为read是不同的

  • 读取分隔符作为输入
  • 将该分隔符分配给变量

在读取字符时,定界符与非定界符一样对待并read计算它们。但是,在read分配分隔符时,它会考虑读取的输入是否为分隔符,如果是分隔符,则将 null 分配给相应的变量。

因此,IFS=将改变将空白分配给变量的行为,并导致分配给空格c而不是空值。

于 2015-08-23T12:17:59.033 回答
2

Usinghexdump可以让我们准确地看到构成输出的字符,所以稍微改变你的查询可能会有所帮助:

(1) 使用普通 IFS 并使用 -N 选项

$ (echo 'a b' | while read -N1 c; do c="$c<"; echo -n "$c"; done | hexdump -C)
00000000  61 3c 3c 62 3c 3c                                 |a<<b<<|
00000006 

在第一种情况下,两者的 read 内置0x0a字符和空格字符都返回空字符串,因为字符在默认 IFS 中,并且 IFS 中的字符在输出中被忽略,原因在 cdarke 的答案中解释。

(2) 使用空 IFS 和 -N 选项

$ (IFS=""; echo 'a b' | while read -N1 c; do c="$c<"; echo -n "$c"; done | hexdump -C)
00000000  61 3c 20 3c 62 3c 0a 3c                              |a< <b<.<|
00000008

在这种情况下,内置命令 read 将匹配 echo 命令输出的四个字符中的每一个,并且0x0a在输出中都可以看到 和 一个空格,因为使用空的 IFS 可以将读取的字符分配给局部变量c

(3) 使用普通 IFS 和 -n 选项

$ (echo 'a b' | while read -n1 c; do c="$c<"; echo -n "$c"; done | hexdump -C)
00000000  61 3c 3c 62 3c 3c                                 |a<<b<<|
00000006 

这给出了与案例 (1) 相同的输出,尽管语义有点不同:两者的 read 内置0x0a字符和空格字符都返回空字符串,因为 (i) 这两个字符都在默认的 IFS 中,并且 (ii ) read 内置的 -n 选项在任何情况下都不会传递尾随0x0a字符

(4) 使用空 IFS 和 -n 选项

$ (IFS=""; echo 'a b' | while read -n1 c; do c="$c<"; echo -n "$c"; done | hexdump -C)
00000000  61 3c 20 3c 62 3c 3c                              |a< <b<<|
00000007

在这里,我们观察到读取 -n 和 -N 选项之间的区别:使用 -n 选项,换行符由 read 内置函数特殊处理并删除,因此0x0a从 IFS 中排除没有机会允许它被传递给局部变量c

于 2015-08-31T14:45:42.693 回答
1

read在读取字符之前无法确定字符是否为定界符(忽略它),并且read必须为 分配一些c,即使该值是空字符串。当分隔符被读取并随后被丢弃时,c必须将 的值设置为something,因此它被分配为空字符串。

这与readused without the -n/-Noptions 一致;分隔符仅在读取后并且不需要设置提供的参数的值时才会被丢弃。最简单的情况是当您不提供任何参数时read

$ read <<< " a b c "
$ echo ">>>$REPLY<<<"
>>> a b c <<<

使用单个显式参数,前导和尾随分隔符被剥离:

$ read line <<< " a b c "
$ echo ">>>$line<<<"
>>>a b c<<<

使用两个参数,第一个分隔符一旦被读取就会被忽略。第二个是保留的,因为字符串只需要拆分成两个词来填充提供的参数。

$ read field1 field2 <<< " a b c """
$ echo ">>>$field1<<<"
>>>a<<<
$ echo ">>>$field2<<<"
>>>b c<<<
于 2015-08-23T20:28:32.057 回答