问题是您的输入文件使用 DOS 行结尾CRLF
而不是 UNIX 行结尾 justLF
并且您正在其上运行 UNIX 工具,因此 UNIX 工具CR
正在操作的数据的剩余部分。当您在文件上运行时CR
,通常表示为\r
并且可以将其视为 control-M ( ^M
),而is和显示为.cat -vE
LF
\n
$
cat -vE
所以你的输入文件不仅仅是:
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
在frodos
某些 UNIX 变体(例如 Ubuntu)中也是如此。
如果您决定使用tr -d '\r'
通常建议的方式,请小心,因为这将删除文件中的所有 \r
s,而不仅仅是每行末尾的那些。
请注意,GNU awk 将允许您通过简单地设置来解析具有 DOS 行结尾的文件RS
:
gawk -v RS='\r\n' '...' file
但其他 awk 不允许这样做,因为 POSIX 只要求 awk 支持单个字符 RS,而大多数其他 awk 将悄悄地截断RS='\r\n'
为RS='\r'
. 您可能需要添加-v BINMODE=3
for gawk 才能看到\r
s,因为底层 C 原语会在某些平台上剥离它们,例如 cygwin。
需要注意的一件事是,由 Excel 等 Windows 工具创建的 CSV 将CRLF
用作行尾,但可以将LF
s 嵌入到 CSV 的特定字段中,例如:
"field1","field2.1
field2.2","field3"
是真的:
"field1","field2.1\nfield2.2","field3"\r\n
因此,如果您只是将\r\n
s 转换为\n
s,那么您将无法再将换行符中的字段内换行符作为行结尾,所以如果您想这样做,我建议您首先将所有字段内换行符转换为其他内容,例如,这将转换所有内部换行符-fieldLFs
到制表符并将所有行尾CRLF
s 转换为LF
s:
gawk -v RS='\r\n' '{gsub(/\n/,"\t")}1' file
在没有 GNU awk 的情况下做类似的练习,但使用其他 awk 时,它涉及组合CR
在读取时不以结尾的行。
另请注意,尽管 CR 是[[:space:]]
POSIX 字符类的一部分,但在使用默认 FS of 时,它不是作为分隔字段包含的空白字符之一" "
,其空白字符只有制表符、空白符和换行符。如果您的输入在 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 行结尾的行的开头/结尾处忽略尾随字段分隔符空格,但如果之前的字符是空格,则它是具有 CRLF 行结尾的行上的最后一个字段\r
:
$ printf 'x y \r\n' | awk '{print $NF}' | cat -Ev
^M$