在Linux上移除Windows换行符(sed vs. awk)

38

有一些分隔符文件,在字段中间(而不是行末)存在不正确放置的换行符,出现为Vim中的^M。它们来自于Centos 6上的freebcp导出MSSQL数据库。将数据转储为十六进制显示\r\n模式:

$ xxd test.txt | grep 0d0a
0000190: 3932 3139 322d 3239 3836 0d0a 0d0a 7c43

我可以使用awk删除它们,但是用sed却做不到。

在awk中,这样做可以完全删除换行符:

awk 'gsub(/\r/,""){printf $0;next}{print}'

但是在sed中,它不能正常工作,会保留换行符:

sed -i 's/\r//g'

这似乎没有任何影响:

sed -i 's/\r\n//g'

即使在sed表达式中使用^M(ctrl+v,ctrl+m),也似乎无效。

对于这种任务来说,sed更容易理解,但我正在努力学习两者。 我是在错误地使用sed,还是存在一些限制?


你尝试过使用引号吗:sed -e s/"^M"//g - Steve
在我的环境中(使用GNU sed 4.2.1),一切都正常。 - ephemient
@ephemient - 你用的是哪种模式?我也有同样版本的sed。 - kermatt
即使使用POSIXLY_CORRECT=1,也要执行sed 's/\r//g'。第二个命令当然没有任何作用,因为\n不是模式空间的一部分。 - ephemient
这个sed命令是删除\r\n模式还是用\n替换它们?在我的系统上,发生的是替换而不是删除。 - kermatt
5个回答

69

你可以使用命令行工具dos2unix


dos2unix input

或者使用“tr”命令:
tr -d '\r' <input >output

实际上,您可以在vim中进行文件格式转换:
:e ++ff=dos
:w ++ff=unix
:e!

:e ++ff=dos
:set ff=unix
:w

编辑

如果您想删除文件中的\r\n序列,请尝试在vim中使用以下命令:

:e ++ff=unix           " <-- make sure open with UNIX format
:%s/\r\n//g            " <-- remove all \r\n
:w                     " <-- save file

您的 awk 解决方案很好。另外还有两个 sed 解决方案:
sed '1h;1!H;$!d;${g;s/\r\n//g}' input
sed ':A;/\r$/{N;bA};s/\r\n//g' input

1
dos2unix 会保留换行符 (\n),我需要完全删除它们。tr 命令只能删除 \r,结果并没有改变。 - kermatt
tr -d '[\r\n]' 将文件转换为一行。它似乎是逐个删除字符。 - kermatt
@MattK 为什么 dos2unix 不起作用?你能发一下你的样本输入/输出文件吗? - kev
dos2unix似乎将\r\n替换为\n。我需要删除\r\n模式,因为文件已经具有Unix行结尾,并且Windows对在行内是垃圾数据。 - kermatt
1
在vim中,甚至在普通的vi中,您也可以通过键入:%s/^V^M//来删除行末的Ctrl-M。 Ctrl-V会导致Ctrl-M被转义,以便您可以将其包含在表达式中。 我经常在FreeBSD和OSX的vi中执行此操作。 - ghoti
显示剩余2条评论

25

我相信某些版本的sed不会将\r识别为一个字符。然而,你可以使用bash的功能来解决这个限制:

echo $string | sed $'s/\r//'

在这里,你让 bash 在传递给 sed 的命令中的 $'...' 结构内将 '\r' 替换为实际的回车字符。(假定您使用的是 bash;其他shell应该有类似的结构。)


这似乎是事实。但我有大量文本组需要处理,大约100MB的文件。在bash中寻找其他解决方法的示例。寻找适用于此情况的方法。 - kermatt
这似乎是正确的路径,但最终,awk似乎是答案。它的语法更复杂,但我提供的正则表达式按预期工作(与Vim中相同)。 - kermatt

10

sed -e 's/\r//g' input_file

这对我很有效。与-i命令不同的是使用了-e命令。
此外,我提到在不同的平台上查看的行为是不同的。我的平台是:sed --version This is not GNU sed version 4.0


7
另一种方法
awk 1 RS='\r\n' ORS=
  • 将记录分隔符设置为\r\n
  • 将输出记录分隔符设置为空字符串
  • 1始终为真,如果没有动作块{print}被使用

0
我整个文件都显示为一行,而不是换行符号"^M"。 对我来说,唯一有效的解决办法是在vi中输入这个命令(不要复制粘贴)。
:%s/\r/\r/g

然后使用'ZZ'保存并退出。
这个命令告诉Vim将每个回车符(\r,显示为^M)替换为换行符。%告诉Vim将该命令应用于文件中的每一行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接