为什么我的工具输出会覆盖自己以及如何修复它?

Ed *_*ton 10 unix awk dos2unix sed

这个问题的目的是提供一个答案,每天的问题,其答案是"你中有DOS行结束",所以我们可以简单地关闭他们,因为这一个的副本,而无需重复同样的答案,令人生厌.

注意:这不是任何现有问题的副本.本问答的目的不仅仅是提供"运行此工具"的答案,而且还要解释这个问题,以便我们可以在这里指出任何有相关问题的人,他们会找到一个明确的解释,为什么他们在这里也被指出作为运行的工具,解决他们的问题.我花了几个小时阅读所有现有的问答,他们都缺乏对问题的解释,可用于解决问题的替代工具,和/或可能的解决方案的优缺点/警告.他们中的一些人已经接受了一些非常危险且永远不应该使用的答案.

现在回到将导致推荐的典型问题:

我有一个包含1行的文件:

what isgoingon
Run Code Online (Sandbox Code Playgroud)

当我使用这个awk脚本打印它来反转字段的顺序时:

awk '{print $2, $1}' file
Run Code Online (Sandbox Code Playgroud)

而不是看到我期望的输出:

isgoingon what
Run Code Online (Sandbox Code Playgroud)

我得到的行应该在行的末尾出现在行的开头,覆盖行开头的一些文本:

 whatngon
Run Code Online (Sandbox Code Playgroud)

或者我将输出拆分为2行:

isgoingon
 what
Run Code Online (Sandbox Code Playgroud)

问题是什么,我该如何解决?

Ed *_*ton 15

问题是您的输入文件使用的是DOS行结尾CRLF而不是UNIX行结尾,LF而您正在运行UNIX工具,因此CR仍然是UNIX工具操作的数据的一部分.CR通常由表示\r,并且可以被看作是一个控制-M( ^M)时运行cat -vE上的文件,而LF\n,并显示为$cat -vE.

所以你的输入文件不仅仅是:

what isgoingon
Run Code Online (Sandbox Code Playgroud)

它实际上是:

what isgoingon\r\n
Run Code Online (Sandbox Code Playgroud)

你可以看到cat -v:

$ cat -vE file
what isgoingon^M$
Run Code Online (Sandbox Code Playgroud)

并且od -c:

$ od -c file
0000000   w   h   a   t       i   s   g   o   i   n   g   o   n  \r  \n
0000020
Run Code Online (Sandbox Code Playgroud)

所以当你在文件上运行像awk这样的UNIX工具(它被视为\n行结尾)时\n,读取该行的行为就会消耗掉,但这会将2个字段保留为:

<what> <isgoingon\r>
Run Code Online (Sandbox Code Playgroud)

请注意\r第二个字段的末尾.\r表示Carriage Return字面上是将光标返回到行首的指令,所以当你这样做时:

print $2, $1
Run Code Online (Sandbox Code Playgroud)

awk将打印isgoingon然后将光标返回到打印前的行的开头,what这就是为什么what看起来会覆盖开头的原因isgoingon.

要解决此问题,请执行以下任一操作:

dos2unix file
sed 's/\r$//' file
awk '{sub(/\r$/,"")}1' file
perl -pe 's/\r$//' file
Run Code Online (Sandbox Code Playgroud)

显然dos2unix是又名frodos一些UNIX变种(如Ubuntu的).

如果您决定tr -d '\r'按照通常的建议使用,请小心,因为这将删除文件中的所有内容 \r,而不仅仅是每行末尾的内容.

请注意,GNU awk将允许您通过简单设置来解析具有DOS行结尾的文件RS:

gawk -v RS='\r\n' '...' file
Run Code Online (Sandbox Code Playgroud)

但是其他awk不允许这样做,因为POSIX只需要awks来支持单个字符RS,而大多数其他awk将悄然截断RS='\r\n'RS='\r'.你可能需要添加-v BINMODE=3for gawk来查看\rs,因为底层的C原语会在某些平台上剥离它们,例如cygwin.

需要注意的一点是,像Excel这样的Windows工具创建的CSV将CRLF用作行结尾,但可以LF嵌入到CSV的特定字段中,例如:

"field1","field2.1
field2.2","field3"
Run Code Online (Sandbox Code Playgroud)

是真的:

"field1","field2.1\nfield2.2","field3"\r\n
Run Code Online (Sandbox Code Playgroud)

因此,如果您只是将\r\ns 转换为\ns,那么您就不能再将换行中的换行符作为行结尾,因此如果您想这样做,我建议首先将所有场内换行符转换为其他内容,例如,这将转换为所有内部-field LFsto tabs并将所有行结尾转换CRLFLFs:

gawk -v RS='\r\n' '{gsub(/\n/,"\t")}1' file
Run Code Online (Sandbox Code Playgroud)

在没有GNU awk的情况下做类似的事情只是作为一个练习,但与其他awks一样,它涉及组合CR在阅读时不会结束的行.

  • 我理解您关于谨慎使用“tr -d '\r'”的言论,但出于专业好奇心:您是否遇到过 Windows CSV 文件在某处具有“\r”的预期有效负载? (2认同)
  • @JamesBrown 这就是我向 @EdMorton 提问的原因。我必须处理大量输入数据,并且在数据中找到一个单独的“\r”使我的验证例程发出“嘟嘟”声。我有一个案例(不是说谎!),几年前有人使用 `\r` 作为列,使用 `\n` 作为行分隔符。:-) (2认同)