Unix 为什么我的工具输出会覆盖自身,如何修复它?

Unix 为什么我的工具输出会覆盖自身,如何修复它?,unix,awk,sed,dos2unix,Unix,Awk,Sed,Dos2unix,这个问题的目的是为回答“您有DOS行结尾”的日常问题提供答案,这样我们就可以简单地将它们作为本问题的副本关闭,而不必重复同样的答案 注意:这不是任何现有问题的重复。本问答的目的不仅仅是提供一个“运行此工具”的答案,而且是解释问题,这样我们就可以在这里向任何人提出相关问题,他们就会清楚地解释为什么在这里提出这些问题,以及运行此工具以解决他们的问题。我花了几个小时阅读了所有现有的问答,它们都缺乏对问题的解释、可用于解决问题的替代工具和/或可能解决方案的优缺点/注意事项。此外,他们中的一些人已经接受了

这个问题的目的是为回答“您有DOS行结尾”的日常问题提供答案,这样我们就可以简单地将它们作为本问题的副本关闭,而不必重复同样的答案

注意:这不是任何现有问题的重复。本问答的目的不仅仅是提供一个“运行此工具”的答案,而且是解释问题,这样我们就可以在这里向任何人提出相关问题,他们就会清楚地解释为什么在这里提出这些问题,以及运行此工具以解决他们的问题。我花了几个小时阅读了所有现有的问答,它们都缺乏对问题的解释、可用于解决问题的替代工具和/或可能解决方案的优缺点/注意事项。此外,他们中的一些人已经接受了非常危险的答案,永远不应该使用

现在回到典型问题,该问题将导致此处的推荐:

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

what isgoingon
当我使用这个awk脚本来反转字段的顺序时:

awk '{print $2, $1}' file
我希望看到的不是输出:

isgoingon what
我得到了应该在行尾的字段,该字段出现在行首,覆盖了行首的一些文本:

 whatngon
或者我将输出分成两行:

isgoingon
 what

问题可能是什么?我该如何解决它?

问题是,您的输入文件使用DOS的
CRLF
行结尾,而不是UNIX的
LF
行结尾,并且您正在运行UNIX工具,因此
CR
仍然是UNIX工具操作的数据的一部分
CR
通常用
\r
表示,当您在文件上运行
cat-vE
,而
LF
\n
时,可以将其视为control-M(
^M
),并与
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
因此,当您在文件上运行一个UNIX工具,如awk(它将
\n
视为行尾)时,
\n
会被读取行的行为消耗掉,但这会留下以下两个字段:

<what> <isgoingon\r>
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'
,请小心,因为这将删除文件中的所有
\r
,而不仅仅是每行末尾的那些

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

gawk -v RS='\r\n' '...' file
但是其他AWK将不允许这样做,因为POSIX只要求AWK支持单个字符RS,而大多数其他AWK将悄悄地将
RS='\r\n'
截断为
RS='\r'
。您可能需要添加
-v BINMODE=3
,以便gawk甚至可以看到
\r
s,因为底层C原语将在某些平台上剥离它们,例如cygwin

需要注意的一点是,由Excel等Windows工具创建的CSV将使用
CRLF
作为行尾,但可以在CSV的特定字段中嵌入
LF
s,例如:

"field1","field2.1
field2.2","field3"
事实上:

"field1","field2.1\nfield2.2","field3"\r\n
因此,如果您只是将
\r\n
s转换为
\n
s,则无法再将字段内的换行符与换行符区分为换行符,因此如果您要这样做,我建议首先将所有字段内换行符转换为其他换行符,例如,这会将所有字段内的
LFs
转换为制表符,并将所有行尾
CRLF
s转换为
LF
s:

gawk -v RS='\r\n' '{gsub(/\n/,"\t")}1' file
在没有GNU awk作为练习的情况下进行类似操作,但在其他awk中,它涉及到在读取时不以
CR
结尾的行的组合。

运行。虽然您可以使用自己编写的代码操纵行尾,但Linux/Unix世界中存在的一些实用程序已经为您完成了这项工作

如果在Fedora系统上
dnf install dos2unix
会将
dos2unix
工具放置到位(如果未安装)

有一个类似的
dos2unix
deb包可用于基于Debian的系统

从编程的角度来看,转换很简单。在文件中的所有字符中搜索序列
\r\n
,并将其替换为
\n

这意味着有几十种方法可以使用几乎所有可以想象的工具从DOS转换到Unix。一种简单的方法是使用命令
tr
,您只需将
\r
替换为零

tr -d '\r' < infile > outfile
tr-d'\r'outfile

对于行尾未知的文件,可以使用中的
\R
。在Unicode或其他平台上,还有更多的行结尾要考虑。
\R
表单是Unicode联盟推荐的字符类,用于表示通用换行符的所有形式

因此,如果您有一个“额外的”,您可以使用regex找到并删除它
s/\R$/\n/
将任何行尾组合规范化为
\n
。或者,您可以使用
s/\R/\n/g
捕获任何“行结束”的概念,并将其标准化为
\n
字符

鉴于:

$ printf "what\risgoingon\r\n" > file
$ od -c file
0000000    w   h   a   t  \r   i   s   g   o   i   n   g   o   n  \r  \n
0000020
Perl和Ruby以及大多数风格的PCRE实现了
\R
与字符串结束断言
$
(多行模式下的行结束):

(请注意,两个单词之间的
\r
被正确地单独保留)

如果您没有
\R
,可以在PCRE中使用相当于
(?>\R\n |\v)

直的
$ printf "what\risgoingon\r\n" > file
$ od -c file
0000000    w   h   a   t  \r   i   s   g   o   i   n   g   o   n  \r  \n
0000020
$ perl -pe 's/\R$/\n/' file | od -c
0000000    w   h   a   t  \r   i   s   g   o   i   n   g   o   n  \n    
0000017
$ ruby -pe '$_.sub!(/\R$/,"\n")' file | od -c
0000000    w   h   a   t  \r   i   s   g   o   i   n   g   o   n  \n    
0000017
$ awk '{sub(/\r$/,"")} 1' file | od -c
0000000    w   h   a   t  \r   i   s   g   o   i   n   g   o   n  \n    
0000017
$ tr -d "\r" < file | od -c
0000000    w   h   a   t   i   s   g   o   i   n   g   o   n  \n        
0000016
$ sed 's/\x0D//' file | od -c   # also sed 's/\r//'
0000000    w   h   a   t  \r   i   s   g   o   i   n   g   o   n  \n    
0000017