如果同一个文件中存在 CR 和 LF,sed 无法区分它们

sed can't differentiate CR from LF if both exist in the same file

我有一个文件,其中 CR (\r) 和 LF (\n) 存在于同一个文件中。

a1 a2 CRLF
b1 LF
b2 CRLF
c1 c2 CRLF

文件需要固定为:

a1 a2 CRLF
b1 b2 CRLF
c1 c2 CRLF

逻辑很简单:删除前面没有空字符串的 CR 的 LF:

sed 's/[^\r]\n//g' input.txt > output.txt

然而,这是行不通的!

我不得不删除所有出现的 LF,并将所有剩余的 CR 替换为 CRLF:

cat input.txt | tr -d '\n' | sed 's/\r/\r\n/g' >output.txt

这让我很烦。为什么 sed 不工作??

sed 在其操作的行中看不到行结尾。

这与 sed 's/\n//' 不给您只有一行的文件的原因相同。

处理换行符 "internally"。

这是 dos2unix/unix2dos/等需要完成的任务。可以更直接的为您办理。

@Etan Reisner 基本上是正确的 - sed 将文本处理为换行符分隔的行,因此您需要跳过一些步骤才能使其直接处理换行符。仅仅因为你可以做到这一点并不意味着它是最干净的方法,但如果你没有其他工具可供使用,这里有一个如何做到这一点的例子:

sed -e 's/[^\r]$/&/' -e te -e b -e :e -e N -e 's/\n//'

这个命令的作用是:

  1. s/[^\r]$/&/ - 将一行末尾的 CR 替换为 ... 本身。
  2. te - 测试和分支:如果先前的替换成功,则分支到指示的标签。 (我们需要它才能成功,这就是为什么它用自己代替)
  3. b - 无条件跳转到脚本末尾
  4. :e - 为之前的 te 命令创建一个标签以跳转到
  5. N - 将下一行追加到模式 space 中。这会产生一个带有嵌入换行符的模式 space。
  6. s/\n// - 删除嵌入的换行符。

我会使用 awk:

awk -v RS='\r\n' 'BEGIN { ORS = RS } { gsub(/\n/, ""); print }'

将记录分隔符 RS 设置为 \r\n,文件将被拆分为由 \r\n 分隔的记录,因此删除这些记录中的换行符会删除所有换行符前面没有 \r。将 ORS(输出记录分隔符)设置为 RS 使得输出文件仍然具有 CRLF 行结尾。

请注意,多字符 RS 并不严格符合 POSIX。不过,最常见的 awk 支持它。

或者有 Perl 方式:

perl -pe 's/(?<!\r)\n//'

这依赖于消极的回顾; (?<!\r) 匹配前面没有 \r 的空字符串。请注意,与 sed 不同,没有 -l 的 Perl 不会从输入中删除换行符,因此不需要特殊技巧来删除它们。