【问题标题】:how to remove text block (pattern) from a file with sed/awk如何使用 sed/awk 从文件中删除文本块(模式)
【发布时间】:2011-11-13 03:15:20
【问题描述】:

我已经导入了数千个文本文件,其中包含我想要删除的一段文本。

它不仅仅是一个文本块,而是一个模式。

<!--
# Translator(s):
#
# username1 <email1>
# username2 <email2>
# usernameN <emailN>
#
-->

如果出现该块,它将列出 1 个或多个用户及其电子邮件地址。

【问题讨论】:

    标签: text sed awk pattern-matching


    【解决方案1】:

    我有另一个小 awk 程序,只需几行代码即可完成任务。它可用于从文件中删除文本模式。可以设置启动和停止正则表达式。

    # This block is a range pattern and captures all lines between( and including )
    # the start '<!--' to the end '-->' and stores the content in record $0. 
    # Record $0 contains every line in the range pattern.
    # awk -f remove_email.awk yourfile
    
    # The if statement is not needed to accomplish the task, but may be useful.
    # It says - if the range patterns in $0 contains a '@' then it will print
    # the string "Found an email..." if uncommented.
    
    # command 'next' will discard the content of the current record and search
    # for the next record.
    # At the same time the awk program begins from the beginning.
    
    
    /<!--/, /-->/ {
        #if( $0 ~ /@/ ){
            # print "Found an email and removed that!"
        #}
    next
    }
    
    # This line prints the body of the file to standard output - if not captured in
    # the block above.
    1 {
        print
    }
    

    将代码保存在“remove_email.awk”中并通过以下方式运行它: awk -f remove_email.awk 你的文件

    【讨论】:

      【解决方案2】:

      此 sed 解决方案可能有效:

       sed '/^<!--/,/^-->/{/^<!--/{h;d};H;/^-->/{x;/^<!--\n# Translator(s):\n#\(\n# [^<]*<email[0-9]\+>\)\+\n#\n-->$/!p};d}' file
      

      另一种选择(也许更好的解决方案?):

      sed '/^<!--/{:a;N;/^-->/M!ba;/^<!--\n# Translator(s):\n#\(\n# \w\+ <[^>]\+>\)+\n#\n-->/d}' file
      

      这会收集以&lt;!-- 开头并以--&gt; 结尾的行,然后在集合上进行模式匹配,即第二行是# Translator(s): 第三行是#,第四行可能还有更多行跟随@ 987654327@,倒数第二行是#,最后一行是--&gt;。如果匹配,则删除整个集合,否则将正常打印。

      【讨论】:

      • 这个解决方案对我有用,但很难解析。您能否解释一下这是在做什么以及为什么?
      • 在我看来,通过 awk 完成这项任务要好得多,如 user2178077 的回答所示。
      • @marbu sed 等效项是 sed '/^&lt;!--/,/^--&gt;/d' file
      • 啊,好的。也就是说,与 sed 解决方案相比,调整 awk 脚本仍然更容易。
      【解决方案3】:

      对于此任务,您需要先行,这通常使用解析器完成。

      另一种解决方案,但不是很有效:

      sed "s/-->/&\n/;s/<!--/\n&/" file |  awk 'BEGIN {RS = "";FS = "\n"}/username/{print}'
      

      HTH 克里斯

      【讨论】:

      • 为什么你认为这是低效的?今天 IT 中的稀缺资源是程序员的时间,而不是计算机的效率。对我来说,一个带有两个易于理解的语句的衬里看起来非常有效;解析器解决方案会是什么样子;-)?祝你好运!
      • 你是对的。这个解决方案将是我的第一次尝试。但对于数千个文件,这可能不够高效。
      • 很公平,我已经忽略了数千个文件的需求。我会说,如果这是 1 次需要数千个文件,那么您的解决方案仍然足够好(在 for 循环内)。如果每天有数千个文件,那么解析器解决方案可能会很有用。 @armenzg:运行时对您来说有多重要?附:克里斯:我对你的回答投了赞成票,但我没有看到 1(我看到了黄色箭头)。也许它会在以后出现。祝大家好运。
      【解决方案4】:
      perl -i.orig -00 -pe 's/<!--\s+#\s*Translator.*?\s-->//gs' file1 file2 file3
      

      【讨论】:

      • -1 这将删除所有以#Translator 开头的注释块。示例:
      • @Dogbane:为什么会这样:这不是所要求的精确任务吗?你的抱怨是什么?
      • 首先它应该匹配“Translator(s):”。其次,您的解决方案没有考虑用户名及其电子邮件地址。
      • @Dogbane:那又怎样?问题描述并没有说它必须。我做了需要做的事。你也不是 OP。
      • @dogbane 显然你和我读这些东西的方式不同。它说如果它在那里,那么它将至少有一个用户。因此,您不必检查它们。你只需要检查块,这就是我所做的。如果你想为用户构建一个解析器,那很好,但是没有 BNF 可以说明确切的内容。
      【解决方案5】:

      如果我正确理解您的问题,这是我的解决方案。将以下内容保存到名为 remove_blocks.awk 的文件中:

      # See the beginning of the block, mark it
      /<!--/ {
          state = "block_started" 
      }
      
      # At the end of the block, if the block does not contain email, print
      # out the whole block.
      /^-->/ {
          if (!block_contains_user_email) {
              for (i = 0; i < count; i++) {
                  print saved_line[i];
              }
              print
          }
      
          count = 0
          block_contains_user_email = 0
          state = ""
          next
      }
      
      # Encounter a block: save the lines and wait until the end of the block
      # to decide if we should print it out
      state == "block_started" {
          saved_line[count++] = $0
          if (NF>=3 && $3 ~ /@/) {
              block_contains_user_email = 1
          }
          next
      }
      
      # For everything else, print the line
      1
      

      假设您的文本文件位于 data.txt(或许多文件中):

      awk -f remove_blocks.awk data.txt
      

      上面的命令将打印出文本文件中的所有内容,减去包含用户电子邮件的块。

      【讨论】:

        猜你喜欢
        • 2023-01-26
        • 1970-01-01
        • 2016-03-28
        • 1970-01-01
        • 2023-03-05
        • 1970-01-01
        • 1970-01-01
        • 2016-02-04
        • 1970-01-01
        相关资源
        最近更新 更多