【问题标题】:Match only the first paragraph using bash使用 bash 仅匹配第一段
【发布时间】:2021-03-01 15:01:45
【问题描述】:

我们有

...包含段落的文件,由 2 个换行符 \r\n\r\n\n\n 分隔。段落本身可能包含单个换行符\r\n\n。目标是使用 Bash 单行来匹配第一段并将其打印到标准输出。

例如:

$ cat foo.txt
Foo
* Bar

Baz
* Foobar

Even more stuff to match here.

结果:

$ cat foo.txt | <some-command>
Foo
* Bar

我已经试过了

...这个正则表达式 (?s)(.+?)(\r?\n){2}|.+?$grep 使用

  • Windows 上的 GIT Bash (GNU grep 3.1),
  • Bash on Lubuntu 20.4.1 LTS (GNU grep 3.4) 和
  • Mac 上的 iTerm+Fish (BSD grep 2.5.1-FreeBSD)。

前两种方法导致:

$ grep -Poz '(?s)(.+?)(\r?\n){2}|.+?$' foo.txt
Foo                                                                                                                          
* Bar

Baz                                                                                                                          
* Foobar

由于 BSD grep 和 GNU grep 之间的差异,Mac 上的方法失败了。

但是

... 在 regex101.com 上,此正则表达式适用于 foo.txt:https://regex101.com/r/uoej8O/1。这可能是由于禁用了global 标志?

【问题讨论】:

  • 当你说 “可能包含单个换行符 \r\n 或 \n。” 你的意思是你在一个文件中混合了两种类型的行尾吗?或者您需要它来处理普通文件,其中一些具有 DOS 行结尾,而其中一些具有 Unix 行结尾?
  • @jas 因为 DOS 和 UNIX 结尾。
  • 在使用任何 Unix shell 工具之前删除 DOS 行尾总是更安全,因为它可能会产生意想不到的结果。

标签: regex bash awk sed grep


【解决方案1】:

这是一个gnu awk量身定制的问题,使用了自定义记录分隔符。我们可以使用自定义RS,将文件数据拆分为两个或多个可选\r,后跟\n

awk -v RS='(\r?\n){2,}' 'NR == 1' file

这个输出:

Foo
* Bar

如果您希望awk 在输入很大时更高效:

awk -v RS='(\r?\n){2,}' '{print; exit}' file

【讨论】:

    【解决方案2】:

    对于 GNU awk,如果段落由 \r\n\r\n\n\n 分隔:

    $ awk -v RS="\r?\n\r?\n" '{print $0;exit}' file
    

    输出:

    Foo
    * Bar
    

    【讨论】:

      【解决方案3】:

      您可以像这样使用 GNU grep

      grep -Poz '(?s)^.+?(?=\R{2}|$)' file
      

      请参阅PCRE regex demo

      详情

      • (?s) - 一个 DOTALL 内联修饰符,使 . 匹配所有字符,包括换行符
      • ^ - 整个字符串的开始
      • .+? - 任何 1 个或多个字符,尽可能少
      • (?=\R{2}|$) - 与紧跟双换行序列 (\R{2}) 或字符串结尾 ($) 的位置匹配的正向前瞻。

      【讨论】:

        【解决方案4】:

        如果您只想要第一段并且段落用换行符分隔,那么这可能有效:

        awk '!NF{ exit } 1' foo.txt
        

        【讨论】:

        • 同理:awk '$0=="" { exit }1' foo.txt
        • 我建议使用$0=="" 而不是awk '!NF { print; exist }' foo.txt
        • 如果第一段由\r\n\r\n分隔,这也会失败。
        【解决方案5】:

        这可能对你有用(GNU sed):

        sed 'N;P;/\n\r\?$/Q;D' file
        

        打开一个两行窗口,打印其中的第一行,如果窗口在行尾包含换行符(带有可选的返回),则退出处理(不打印任何其他内容)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-08-04
          • 2019-05-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多