为什么 sed 's/\r\n/\r/g' 没有按预期工作?

Why doesn't sed 's/\r\n/\r/g' work as expected?

在正常的 windows 到 unix 的转换中,您可以执行类似 sed s/\r//g 的操作,从流中删除 \r 字符。

但我正在尝试转换可能是 mac 编码 (\r) 或 windows 编码 (\r\n) 的文件的结尾。所以我不能只删除 \r,因为它会删除 mac 结尾(如果有的话)。我必须先 "canonicalize" 行结束字符。此规范化步骤从 \r\n 转换为 \r(之后我将 \r 转换为 \n)。然而,我无法用 sed 解决这一步。我试过这样的事情:

$> echo -e "foo\r\nbar" | sed 's/\r\n/\r/g' | xxd -c 24 -g 1
00000000: 66 6f 6f 0d 0a 62 61 72 0a            foo..bar.

我是这样用bbe解决的:

$> echo -e "foo\r\nbar" | bbe -e 's/\r\n/\r/g' | xxd -c 24 -g 1
00000000: 66 6f 6f 0d 62 61 72 0a               foo.bar.

是否可以用 sed 做同样的事情?

sed 默认在 \n 上拆分输入,因此 \n 永远不会以模式 space 结束。但是,如果您使用 GNU sed,您可以使用 -z/--null-data 选项使 sed 将输入视为 NUL 字符分隔行:

$ echo -e "foo\r\nbar" | sed -z 's/\r\n/\r/g' | hd
00000000  66 6f 6f 0d 62 61 72 0a                           |foo.bar.|

或者,在 POSIX sed 中,您可以将所有行附加到模式 space 中(其中 N command一个循环),有效地将完整文件复制到模式 space,然后执行替换:

$ echo -e "foo\r\nbar" | sed -n ':a;N;ta; s/\r\n/\r/g; p' | hd
00000000  66 6f 6f 0d 62 61 72 0a                           |foo.bar.|

您可以使用 perl,它不会像 sed/awk 那样删除记录分隔符。此解决方案不需要对整个文件进行 slurped

$ echo -e 'foo\r\nbar' | perl -pe 's/\r\n/\r/' | xxd -c 24 -g 1
00000000: 66 6f 6f 0d 62 61 72 0a                foo.bar.

请注意,不需要 g 修饰符,因为 \n 每条记录只能出现一次