【问题标题】:Does awk CR LF handling break on cygwin?awk CR LF 处理是否会在 cygwin 上中断?
【发布时间】:2014-08-06 17:26:57
【问题描述】:

在 Linux 上,它按预期运行:

$ echo -e "line1\r\nline2"|awk -v RS="\r\n" '/^line/ {print "awk: "$0}'
awk: line1
awk: line2

但在 windows 下,\r 被删除(awk 认为这一行):

窗户:

$ echo -e "line1\r\nline2"|awk -v RS="\r\n" '/^line/ {print "awk: "$0}'
awk: line1
line2

Windows GNU awk 4.0.1 Linux GNU awk 3.1.8

来自@EdMorton 的编辑(对不起,如果这是一个不需要的添加,但我认为它可能有助于证明问题):

考虑这个 RS 设置和输入(在 cygwin 上):

$ awk 'BEGIN{printf "\"%s\"\n", RS}' | cat -v
"
"
$ echo -e "line1\r\nline2" | cat -v
line1^M
line2

这是带有 gawk 的 Solaris:

$ echo -e "line1\r\nline2" | awk '1' | cat -v   
line1^M
line2

这是带有 gawk 的 cygwin:

$ echo -e "line1\r\nline2" | awk '1' | cat -v
line1
line2

RS 只是它的默认换行符,那么 cygwin 中的 control-M 去了哪里?

【问题讨论】:

  • 是的,我也在 cygwin 中看到过。我懒得去调查或调试它,而且我很少有文件中有\rs,所以必要时我只是在它前面贴了一个tr -d '\r' | awk ...以删除\rs。不过,希望有人能提供一些启示 - 好问题!
  • 你试过-v IRS 吗?注意 I 输入
  • awk -v IRS=something?这会将名为IRS 的未使用的、用户定义的awk 变量设置为something。对这个问题没有影响。
  • @EdMorton 确实,我没有测试它。我只是假设与 FS 一样。
  • 问题似乎是awk 永远不会匹配\r 作为记录分隔符,更不用说\r\n。我尝试了不同的符号,例如\015\x0d,但它仍然不匹配。我还通过使用文件作为输入而不是来自echo 的管道来排除echo 是罪魁祸首。看来awk 确实无法匹配\r

标签: linux bash awk


【解决方案1】:

我刚刚咨询了 Arnold Robbins(gawk 的提供者),答案是它是由 C 库完成的,要阻止它发生,您应该将 awk BINMODE 变量设置为 3:

$ echo -e "line1\r\nline2" | awk '1' | cat -v
line1
line2

$ echo -e "line1\r\nline2" | awk -v BINMODE=3 '1' | cat -v
line1^M
line2

如果有兴趣,请参阅手册页了解更多信息。

【讨论】:

  • 使用 BINMODE 在 Linux 上也可以使用,尽管我不想假设这就是所有这些的原因。或许 Arnold 可以提出修复建议(或许将其设为 Windows 的默认设置)。
  • 我认为这不仅仅是 gawk 的问题,让 gawk 解决它可能会破坏现有脚本和/或引入与其他功能/工具的冲突等,所以我什至不会问阿诺德看看这个。我很高兴有一些有用的东西!
【解决方案2】:

似乎问题是 awk 在 Cygwin 下特定的。
我尝试了一些不同的方法,似乎awk 正在默默地处理输入数据中用\n 替换\r\n

如果我们只是让awk 重复未修改的文本,它会在不询问的情况下“清理”回车:

$ echo -e "line1\r\nline2" | od -a
0000000   l   i   n   e   1  cr  nl   l   i   n   e   2  nl
0000015

$ echo -e "line1\r\nline2" | awk '{ print $0; }' | od -a
0000000   l   i   n   e   1  nl   l   i   n   e   2  nl
0000014

但是,它会保持其他回车不变:

$ echo -e "Test\rTesting\r\nTester\rTested" | awk '{ print $0; }' | od -a
0000000   T   e   s   t  cr   T   e   s   t   i   n   g  nl   T   e   s
0000020   t   e   r  cr   T   e   s   t   e   d  nl
0000033

使用_ 的自定义记录分隔符最终使回车保持不变:

$ echo -e "Testing\r_Tested" | awk -v RS="_" '{ print $0; }' | od -a
0000000   T   e   s   t   i   n   g  cr  nl   T   e   s   t   e   d  nl
0000020  nl
0000021

最有说服力的例子是在数据中包含\r\n,但不是作为记录分隔符:

$ echo -e "Testing\r\nTested_Hello_World" | awk -v RS="_" '{ print $0; }' | od -a
0000000   T   e   s   t   i   n   g  nl   T   e   s   t   e   d  nl   H
0000020   e   l   l   o  nl   W   o   r   l   d  nl  nl
0000034

awk 在输入数据中盲目地将\r\n 转换为\n,即使我们没有要求它这样做。

这种替换似乎是在应用记录分离之前发生的,这就解释了为什么RS="\r\n" 从不匹配任何东西。当awk 正在寻找\r\n 时,它已经在输入数据中将其替换为\n

【讨论】:

  • +1;伟大的侦探。 MSYS 表现出相同的行为。 sed 至少在 MSYS 上也表现出这种行为,大概也在 Cygwin 上;试试sed '' <<<$'line1\r\nline2' | od -a
  • 这不是我们开始并寻找“为什么”和“我们如何阻止它这样做”的原因吗???
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-07
  • 2011-03-07
  • 1970-01-01
  • 2019-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多