AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 720025
Accepted
Peter Petigru
Peter Petigru
Asked: 2022-10-07 07:32:58 +0800 CST2022-10-07 07:32:58 +0800 CST 2022-10-07 07:32:58 +0800 CST

如何grep除匹配项和上一行之外的所有内容

  • 772

我有一个文本文件,并且我有一个不希望 grep 匹配的模式。问题是,我也希望之前的行不匹配。

我的文件:

line 1
line 2
pattern
line 4

我试过cat file | grep -v pattern了,它输出:

line 1
line 2
line 4

然后我尝试cat file | grep -B 1 pattern了,它输出:

line 2
pattern

但是,当我同时使用它们时cat file | grep -v -B 1 pattern,我得到:

line 2

我怎样才能使输出为:

line 1
line 4
shell-script text-processing
  • 7 7 个回答
  • 556 Views

7 个回答

  • Voted
  1. Best Answer
    Kusalananda
    2022-10-07T10:54:19+08:002022-10-07T10:54:19+08:00

    我倾向于只在grep从文件中提取单行时使用,所以当我需要在文本中执行更复杂的编辑时,我会使用其他工具。

    这里的所有解决方案都假定该模式可能在文本中出现多次,并将删除出现该模式的行和紧接在它们之前的行。如果模式在连续行上匹配,前两个解决方案将出现问题。


    您可以使用sed匹配模式/pattern/并让其触发命令Nand d,它将下一行附加到缓冲区,然后丢弃两者:

    sed '/pattern/ { N; d; }' file
    

    由于您想丢弃模式匹配之前的行,因此我们将数据向后馈入sed,从最后一行开始并移至文件的开头。然后我们在sed完成后再次反转数据。

    tac file | sed '/pattern/ { N; d; }' | tac
    

    该tac实用程序是 GNU coreutils 的一部分。大多数非 GNU 系统可以用来tail -r代替tac(查看您的tail(1)手册)。

    如果模式匹配两个连续的行,这将无法删除第一行之前的行(因为第一行将被删除)。


    使用ed编辑器:

    printf '%s\n' 'g/pattern/ -1,. d' ,p Q | ed -s file
    

    这会将命令应用于g/pattern/ -1,. d文件的内容。此命令搜索与 匹配的每一行,pattern然后删除该行及其之前的行。

    最终,p和Q编辑命令打印整个文件并退出编辑器而不保存。

    如果模式匹配两个连续的行,这将在删除第一行之前的行之后删除成为第二行之前的行。

    (最后一句我写的时候是对的,但显然是只写句。)


    我们还可以使用grep它的非标准但通常实现-B的选项来为我们提供需要删除的行号。这些数字可以转换为sed我们在原始数据上运行的脚本:

    grep -n -B1 'pattern' file | sed 's/[:-].*/d/' | sed -f /dev/stdin file
    

    给定问题中的文本,该grep命令将输出

    2-line 2
    3:pattern
    

    ...并且第一个sed命令将其转换为sed编辑命令2d,然后是3d(“删除第 2 行和第 3 行”)。管道中的最后一个sed命令采用此编辑脚本并将其应用于原始文本。

    这个变体对于匹配模式的连续行没有问题,因为它使用一种 2-pass 方法,首先找到所有应该删除的行,然后删除它们(而不是在第一次阅读文本时删除行)。

    • 6
  2. Ed Morton
    2022-10-07T16:45:45+08:002022-10-07T16:45:45+08:00

    使用带有 tac 的任何 awk,您可以在匹配模式之前删除任意数量的行:

    $ tac file | awk '/pattern/{c=2} !(c&&c--)' file | tac
    line 2
    line 1
    

    只需更改c=2或c=5您要删除的任何数量的行,直到并包括匹配的行,例如删除包含数字 97 的行和它之前的 94 行:

    $ seq 100 | tac | awk '/97/{c=95} !(c&&c--)' | tac
    1
    2
    98
    99
    100
    

    现在尝试使用 sed 而不是 awk :-)。

    请参阅print-with-sed-or-awk-a-line-following-a-matching-pattern以了解有关此习惯用法和其他相关习惯用法的说明。

    • 5
  3. Edgar Magallon
    2022-10-07T08:18:18+08:002022-10-07T08:18:18+08:00

    注意:此代码仅在file中不存在与 的输出匹配的每行的重复行或子字符串时才有效grep -B1 pattern file。

    例如,如果file包含以下行:

    line 1
    line 2
    line 2
    pattern
    line 1 line 2
    line 3
    

    而且我使用grep -B1 pattern file | grep -v "$(cat)" file的输出不会像你预期的那样:

    line 1
    line 3
    

    解决这个问题的最好方法是使用Kusalananda 的答案

    解决方案(这仅适用于我上面解释的没有重复行或子字符串的情况)

    这bash对我有用(我认为有更好的方法):

    grep -B1 pattern file | grep -v "$(cat)" file
    

    在zsh上面的命令中将不起作用。我不知道为什么。但是你可以使用:

    grep -B1 pattern file | { val="$(cat)" ; grep -v "$val" file; }
    

    PS你不必使用cat your_file | grep pattern那是多余的。你应该使用grep pattern your_file

    • 1
  4. JoL
    2022-10-08T03:46:56+08:002022-10-08T03:46:56+08:00

    Kusalananda 和 Ed Morton 的解决方案是最简单和最实用的,但它们需要在开始之前阅读内容两次或完整阅读内容。管道不能被重新读取,它们也不总是有限的。适用于任何文本流的解决方案可能是这样的:

    $ awk -v pat='^pattern$' '
      function set_prev() { prev_present = 1; prev = $0 }
      NR == 1 { set_prev() }
      $0 ~ pat { prev_present = 0; next }
      NR != 1 { if (prev_present) print prev; set_prev() }
      END { if ($0 !~ pat) print }
    ' << EOF
    pattern
    line 1
    line 2
    pattern
    pattern
    line 4
    line 5
    pattern
    EOF
    line 1
    line 4
    

    如果你想让不打印的行数可变,那就有点复杂了:

    $ awk -v n=2 -v pat='^pattern$' '
      function ring_empty() { i = 0; ring_is_full = 0 }
      function ring_is_empty() { return i == 0 && !ring_is_full }
      function ring_add() { ring[i++%n] = $0; ring_is_full = i >= n }
      function ring_starting_index() { return ring_is_full ? i%n : 0 }
      function ring_print_oldest() { print ring[ring_starting_index()] }
      function ring_print_all() {
        if (ring_is_empty()) return
        j = ring_starting_index()
        do {
          print ring[j%n]
        } while (++j%n != i%n)
      }
      function ring_push_out() {
        if (ring_is_full) ring_print_oldest()
        ring_add()
      }
    
      { ring_push_out() }
      $0 ~ pat { ring_empty() }
      END { ring_print_all() }
    ' << EOF
    pattern
    line 1
    line 2
    pattern
    pattern
    line 4
    line 5
    pattern
    EOF
    line 1
    line 4
    

    顺便说一句,这些解决方案都没有连续匹配的问题。

    • 1
  5. Stéphane Chazelas
    2022-10-09T08:25:00+08:002022-10-09T08:25:00+08:00

    您可以使用pcregrep它的Multiline 模式:

    pcregrep -Mv '\n.*pattern'
    

    请注意,如果第一行与模式匹配,则不会被删除。这可以通过使用来解决:

    pcregrep -Mv '(\n)?.*pattern'
    

    ((...)周围\n显然是必要的,我不知道为什么它不适用于此处\n?.*pattern的[\n]?.*pattern8.39 版本)。

    • 1
  6. jubilatious1
    2022-10-08T18:00:23+08:002022-10-08T18:00:23+08:00

    使用Raku(以前称为 Perl_6)

    raku -e 'for lines.join("\n") { print .subst(:global, / \N* \n pattern [ \n | $ ]/) };'     
    
    #OR
    
    raku -e 'for lines.join("\n") { print .split(/ \N* \n pattern [ \n | $ ] /).join };'      
    

    前两个答案(上图)基本上检测到两行模式,并将其删除。因此,不会处理连续出现的单词pattern,也不会处理pattern第一行中出现的单词。对于这两个答案,lines都是从文件中读入并 在换行符join上一起编辑(因为默认情况下为 autochomps)。然后搜索所需的两行正则表达式和 1)。ituted(什么都没有,即删除)或 2)。在两行正则表达式上并ed out 。\nlinessubstsplitjoinput

    接下来的两个答案(如下)处理pattern第一行中的出现,以及处理单词的连续出现pattern。[\N* \n]?他们在正则表达式的开头使用分组:

    raku -e 'for lines.join("\n") { print .subst(:global, / [\N* \n]?  pattern [ \n | $ ]/) };'     
    
    #OR
    
    raku -e 'for lines.join("\n") { print .split(/ [\N* \n]?  pattern [ \n | $ ] /).join };'      
    

    样本输入:

    pattern
    line 1
    line 2
    pattern
    pattern
    line 5
    line 6
    pattern
    

    示例输出(删除 2 行正则表达式的前 2 个示例):

    pattern
    line 1
    pattern
    line 5
    

    示例输出(示例 3 和 4 还处理pattern第一行以及连续出现的pattern):

    line 1
    line 5
    

    仅供参考:Raku 的lines例程被宣传为懒惰,因此可能无需先读入整个文件即可分析文件。有关 Rakulines例行程序的评论,请参阅下面的 URL。

    https://speakerdeck.com/util/reading-files-cant-be-this-simple
    https://raku.org

    特别感谢用户 @JoL 在此答案中对原始正则表达式进行了深刻的批评。

    • 0
  7. vik
    2022-10-07T08:15:43+08:002022-10-07T08:15:43+08:00

    您可以将包含pattern该行及其上方的行存储在一个变量中。然后你可以使用这个变量在你的文件中再次 grep。

    match=$(grep pattern -B1 file)
    grep "$match" -v file
    
    • -2

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 按分隔符拆分并连接字符串问题

  • 多行文件洗牌

  • MySQL Select with function IN () with bash array

  • 如何更改字符大小写(从小到大,反之亦然)?同时[重复]

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve