在 mac 上匹配 sed 中的重复字符

match repeated character in sed on mac

我正在尝试查找 3 个或更多新行的所有实例,并将它们替换为仅 2 个新行(想象一个带有 wayyy 太多白色的文件 space)。我正在使用 sed,但如果更容易的话,可以使用 awk 或类似的答案。

注意:我在 mac 上,所以 sed 与在 linux 上略有不同(BSD vs GNU)

我的实际目标是换行,但我根本无法让它工作,所以为了简单起见,我试图匹配 3 次或更多次重复的 bla 并将其替换为 BLA.

创建一个名为 stupid.txt 的示例文件:

$ cat stupid.txt

blablabla
$

我的理解是,您使用正则表达式 thing{i,}.
匹配 i 或更多事物 我试过这个的变体来匹配 3 blas 但没有运气:

cat stupid.txt | sed 's/bla{3,}/BLA/g'      # simplest way
cat stupid.txt | sed 's/bla\{3,\}/BLA/g'    # escape curly brackets
cat stupid.txt | sed -E 's/bla{3,}/BLA/g'   # use extended regular expressions
cat stupid.txt | sed -E 's/bla\{3,\}/BLA/g' # use -E and escape brackets

现在我想不出还能尝试什么了!

thing{3,} 匹配 thinggg。使用 (..) 对事物进行分组,使量词适用于您想要的事物:

$ echo blablabla | sed -E 's/(bla){3}/BLA/g'
BLA
sed -E 's/bla{3,}/BLA/g' 

以上匹配 bl 后跟 a 的三个或更多重复。这不是你想要的。看起来您实际上想要 bla 的三个或更多重复。如果是这样,则替换:

$ sed -E 's/bla{3,}/BLA/g' stupid.txt
blablabla

与:

$ sed -E 's/(bla){3,}/BLA/g' stupid.txt
BLA

不过,以上内容并不能直接帮助您完成换行任务,因为默认情况下,sed 一次只读取一行。

替换换行符

让我们考虑这个文件,它在 12 之间有 3 个换行符:

$ cat file.txt

1



3

要用一个换行符替换任何出现的三个或更多换行符:

$ sed -E 'H;1h;$!d;x; s/\n{3,}/\n/g' file.txt

1
3

工作原理:

  • H;1h;$!d;x

    这一系列复杂的命令会读入整个文件。大概是 最简单的就是将其视为成语。如果你真的想知道 血淋淋的细节:

    • H - 追加当前行以保留 space
    • 1h - 如果这是第一行,覆盖保持 space 有了它
    • $!d - 如果这不是最后一行,删除模式 space 并跳转到下一行。
    • x - 交换保留和模式 space 以将整个文件放入 模式 space
  • s/\n{3,}/\n/g

    这会将三个或更多换行符的所有序列替换为单个换行符。

备用

上述解决方案一次读入整个文件。对于大(千兆字节)文件,这可能是一个缺点。这种替代方法避免了:

$ sed -E '/^$/{:a; N; /\n$/ba; s/\n{3,}([^\n]*)//}' file.txt # GNU only

1
3

工作原理:

  • /^$/{...}

    这将选择空行。对于空行和只有空行,执行大括号中的命令,它们是:

  • :a

    这定义了一个标签 a.

  • N

    这会将文件的下一行读入模式 space,与前一行之间用换行符分隔。

  • /\n$/ba

    如果读入的最后一行为空,分支(跳转)到标签a

  • s/\n{3,}([^\n]*)//

    如果我们没有分支,则执行此替换,删除多余的换行符。

BSD 版本:我没有 BSD 系统来测试这个,但我猜:

sed -E -e '/^$/{:a' -e N -e '/\n$/ba' -e 's/\n{3,}([^\n]*)//}' file.txt

如果可以接受整个文件:

perl -0777pe 's/(\n){3,}/\n\n/g' newlines.txt

在哪里你应该用任何合适的换行序列替换 \n

-0777 告诉 perl 不要将每一行分成自己的记录,这允许跨行工作的正则表达式起作用。

如果您对结果满意,-i 会导致 perl 就地替换文件而不是输出到标准输出:

perl -i -0777pe 's/(\n){3,}/\n\n/g' newlines.txt

您也可以这样做:-i~ 创建具有给定后缀的备份文件(在本例中为 ~)。

如果不能接受整个文件:

perl -ne 'if (/^$/) {$i++}else{$i=0}print if $i<3' newlines.txt

这将打印不是第三个(或更高)连续空行的任何行。 -i 同样适​​用于此。

ps--MacOS 自带 perl。

要只保留 2 个换行符,你可以试试这个 sed

sed '
  /^$/!b
  N
  /../b
  h
  :A
  y/\n/@/
  /^@$/!bB
  s/@//
  $bB
  N
  bA
  :B
  s/^@//
  /./ {
    x
    G
    b
  }
  g
' infile

/^$/!b 如果是空行就不要打印

N 换行

/../b 如果这个新行不为空打印 2 行

h 将 2 个空行保留在保持缓冲区中

:A标签A

此时模式缓冲区中始终有 2 行,第一行是空的

y/\n/@/ 将 \n 替换为 @(您可以选择文件中不存在的其他字符)

/^@$/!bB 如果第二行不为空则跳转到B

s/@// 删除@

$bB 如果是最后一行跳转到B

此时模式中有1个空行space

N 获取最后一行

bA跳转到A

:B标签B

s/^@//去掉行首的@

/./ { 如果最后一行不为空

x 交换模式并保持缓冲区

G 将保持缓冲区添加到模式 space

b跳到结尾

}

g 用 hold space

替换模式 space(空)

打印图案space