问题是您的输入文件使用 DOS 行尾 CRLF 而不是 UNIX 行尾 LF 并且您正在其上运行 UNIX 工具,因此 CR 仍然是正在操作的数据的一部分通过 UNIX 工具。 CR 通常用\r 表示,当您在文件上运行cat -vE 而LF 是\n 并显示为$ 和@987654332 时,可以看作是一个控制-M (^M) @。
所以您的输入文件不只是:
what isgoingon
原来是这样的:
what isgoingon\r\n
如您所见,cat -v:
$ cat -vE file
what isgoingon^M$
和od -c:
$ od -c file
0000000 w h a t i s g o i n g o n \r \n
0000020
因此,当您在文件上运行诸如 awk 之类的 UNIX 工具(将 \n 视为行结尾)时,\n 会被读取行所消耗,但这会将 2 个字段保留为:
<what> <isgoingon\r>
注意第二个字段末尾的\r。 \r 表示 Carriage Return 字面意思是将光标返回到行首的指令,所以当你这样做时:
print $2, $1
awk 将打印isgoingon,然后在打印what 之前将光标返回到行首,这就是为什么what 似乎会覆盖isgoingon 的开头。
要解决此问题,请执行以下任一操作:
dos2unix file
sed 's/\r$//' file
awk '{sub(/\r$/,"")}1' file
perl -pe 's/\r$//' file
显然dos2unix 在某些 UNIX 变体(例如 Ubuntu)中又称为 frodos。
如果您决定使用 tr -d '\r' 时要小心,因为这会删除文件中的所有 \rs,而不仅仅是每行末尾的那些。
请注意,GNU awk 将允许您通过简单地设置 RS 来解析具有 DOS 行结尾的文件:
gawk -v RS='\r\n' '...' file
但其他 awk 不允许这样做,因为 POSIX 只要求 awk 支持单个字符 RS,而大多数其他 awk 会悄悄地将RS='\r\n' 截断为RS='\r'。您可能需要为 gawk 添加 -v BINMODE=3 才能看到 \rs,尽管底层 C 原语会在某些平台上剥离它们,例如cygwin。
需要注意的一点是,由 Excel 等 Windows 工具创建的 CSV 将使用 CRLF 作为行尾,但可以将 LFs 嵌入到 CSV 的特定字段中,例如:
"field1","field2.1
field2.2","field3"
真的是:
"field1","field2.1\nfield2.2","field3"\r\n
因此,如果您只是将\r\ns 转换为\ns,那么您将无法再将换行符中的字段内换行符作为行尾,所以如果您想这样做,我建议您将所有字段内换行符转换为某些内容否则首先,例如这会将所有字段内LFs 转换为制表符,并将所有以CRLFs 结尾的行转换为LFs:
gawk -v RS='\r\n' '{gsub(/\n/,"\t")}1' file
在没有 GNU awk 的情况下做类似的练习,但对于其他 awk,它涉及在读取时组合不以 CR 结尾的行。
还请注意,虽然 CR 是 [[:space:]] POSIX 字符类的一部分,但在使用 " " 的默认 FS 时,它不是作为分隔字段包含的空白字符之一,其空白字符只是制表符,空白, 和换行符。如果您的输入在 CRLF 之前可以有空格,这可能会导致令人困惑的结果:
$ printf 'x y \n'
x y
$ printf 'x y \n' | awk '{print $NF}'
y
$
$ printf 'x y \r\n'
x y
$ printf 'x y \r\n' | awk '{print $NF}'
$
这是因为在具有 LF 行结尾的行的开头/结尾处忽略尾随字段分隔符空格,但 \r 是 具有 CRLF 行结尾的行上的最后一个字段,如果空格之前的字符:
$ printf 'x y \r\n' | awk '{print $NF}' | cat -Ev
^M$