为什么 `awk 1 RS=, <<< "1,2"` 多写一行?

Why is `awk 1 RS=, <<< "1,2"` writing an extra new line?

一个问题,我在其中使用了一些 awk 技巧将逗号转换为换行:

awk 1 RS=, file

但是,我随后注意到这在输出的末尾引入了一个额外的新行:

$ cat a
1,2
$ awk 1 RS=, a
1
2
             # one extra line
$ awk 1 RS=, <<< "1,2"
1
2
             # one extra line

因为 1{print [=18=]} 的简写,所以我决定看看发生了什么:

$ awk '{print [=12=], "hey"}' RS=, <<< "1,2"
1 hey
2
 hey

所以是的,显然拆分已经完成,但由于某种原因,第二条记录包含在 2 后面跟一个新行。是的,awk 只看到两条记录:

$ awk '{print NR}' RS=, <<< "1,2"
1
2

对我来说这是有道理的,因为 echo 和 here-strings 在输出的末尾添加了这样的新行,而 printf 则没有。并且有效地与 printf:

一起工作
$ awk '{print [=14=], "hey"}' RS=, < <(printf "1,2")
1 hey
2 hey         # no more lines after this

好的,我说:那只是追加到字符串末尾的新行的问题。

但后来...我发现情况并非总是如此,我的困惑变得更大:

$ awk '{print [=15=], "hey"}' <<< "1,2"
1,2 hey         # no more lines after this

所以我的问题是:RS=, 做了什么导致追加这个额外的新行?

这是输入流中的换行符。

$ awk 1 RS=, < <(echo -n 1,2)

1
2

输出中不会有额外的换行符。但是,执行此操作的标准方法是使用 tr

$ tr ',' '\n' < file

比较

$ echo 1,2 | awk 1 RS=,
1
2

$ echo 1,2 | tr ',' '\n'
1
2

Awk 处理每条记录,自动从末尾删除记录分隔符。如果您将其更改为换行符以外的其他内容,这意味着它不会被删除,因此您最终会遇到这种行为。

你的 "record count" 是 2,即使你只有一个 , 但在这个例子中它也是 2(希望不会让这更混乱!):

$ printf 'a\nb' | awk '{print NR}'
1
2

添加换行符的不是 awk,而是 <<<。如果 shell 没有在您使用 <<< 指定的文本末尾添加终止换行符,那么结果将不是每个 POSIX 的文本 "file" 等依赖任何试图解析它的工具的未定义行为。

因此,当您编写 command <<< 'foo' 时,command 看到的不是 foo,而是 foo\n,因此在您的命令行中:

awk 1 RS=, <<< "1,2"

awk 看到的是 1,2\n,当您将其拆分为 , 处的记录时,您将获得 1 的第一条记录和 2\n 的第二条记录。