当行(来自管道)包含 GNU sed 模式时,用 \r 替换 \n

Replace \n by \r when line (comming from a pipe) contains pattern with GNU sed

当每一行(来自管道)以指定的模式开始时,我想使用 GNU sed 将 \n 替换为 \r,但我的 sed 命令似乎不起作用:

$ cat mpv_all.log | sed -z '/^AV:/s/\n/\r/g'

你能帮帮我吗?

问题是 -z 导致整个文件被一次读入。因此,^AV: 仅在文件以 AV: 开头时匹配。您可能希望在每一行上匹配 AV:。在这种情况下,尝试:

sed -Ez ':a; s/((^|[\n\r])AV:[^\n\r]*)\n/\r/g ;ta' mpv_all.log

工作原理:

  • -E 打开扩展正则表达式。

  • :a 定义标签 a.

  • 在以 AV:.

    [=43 开头的任何行的末尾,
  • s/((^|[\n\r])AV:[^\n\r]*)\n/\r/g\n 替换为 \r =]

  • ta 告诉 sed 返回到标签 a 如果先前的替换命令导致任何更改。这是必要的,因为模式可以重叠,并且 s 命令的 g 修饰符不会重叠。

如果 awk 也是一个选项:

awk '{ORS=/^AV:/?"\r":"\n"}1' file

ORSoutput record separator;默认设置为 \n。但在上面的脚本中,它的值动态更改为 \r\n,具体取决于输入行是否匹配 /^AV:/(即以 AV: 开头)。

ORS=/^AV:/?"\r":"\n" 是 shorthand 对于

if (/^AV:/)
  ORS="\r"
else
  ORS="\n"

脚本末尾的1代表{print}

只是给出一个 perl 实现:

$ perl -p -e '$| = 1;s/\n/\r/g if $_ =~ /^AV:/;s/Saving state/\nSaving state/;' mpv_all.log
  • -p 在文件的每一行循环
  • -e 运行引号中的 perl 命令
  • $| = 1; Perl 在每一行刷新缓冲区
  • s/\n/\r/g if $_ =~ /^AV:/; 在以 AV:
  • 开头的每一行中将 \n 替换为 \r
  • 可选:s/Saving state/\nSaving state/;Saving state 行放在下一行,以免覆盖包含 AV:
  • 的最后一行