【问题标题】:sed one-liner - Find delimiter pair surrounding keywordsed one-liner - 查找关键字周围的分隔符对
【发布时间】:2012-07-20 05:39:55
【问题描述】:

我通常处理大型 XML 文件,并且通常通过 grep 进行字数统计以确认某些统计数据。

例如,我想通过以下方式确保在单个 xml 文件中至少有五个 widget 实例:

cat test.xml | grep -ic widget

另外,我只是希望能够记录widget 出现的行,即:

cat test.xml | grep -i widget > ~/log.txt

然而,我真正需要的关键信息是widget 出现的XML 代码块。示例文件可能如下所示:

<test> blah blah
  blah blah blah
  widget
  blah blah blah
</test>

<formula>
  blah
  <details> 
    widget
  </details>
</formula>

我正在尝试从上面的示例文本中获取以下输出,即:

<test>widget</test>

<formula>widget</formula>

实际上,我正在尝试获取具有最高级别标记标记的单行,这些标记标记适用于围绕任意字符串 widget 的 XML 文本/代码块。

是否有人对通过命令行单行执行此操作有任何建议?

谢谢。

【问题讨论】:

  • 看看this post。也许你有一些想法。

标签: xml bash sed grep


【解决方案1】:

同时使用sedawk 的非优雅方式:

sed -ne '/[Ww][Ii][Dd][Gg][Ee][Tt]/,/^<\// {//p}' file.txt | awk 'NR%2==1 { sub(/^[ \t]+/, ""); search = $0 } NR%2==0 { end = $0; sub(/^<\//, "<"); printf "%s%s%s\n", $0, search, end }'

结果:

<test>widget</test>
<formula>widget</formula>

解释:

## The sed pipe:

sed -ne '/[Ww][Ii][Dd][Gg][Ee][Tt]/,/^<\// {//p}'
## This finds the widget pattern, ignoring case, then finds the last, 
## highest level markup tag (these must match the start of the line)
## Ultimately, this prints two lines for each pattern match

## Now the awk pipe:

NR%2==1 { sub(/^[ \t]+/, ""); search = $0 }
## This takes the first line (the widget pattern) and removes leading
## whitespace, saving the pattern in 'search'

NR%2==0 { end = $0; sub(/^<\//, "<"); printf "%s%s%s\n", $0, search, end }
## This finds the next line (which is even), and stores the markup tag in 'end'
## We then remove the slash from this tag and print it, the widget pattern, and
## the saved markup tag

HTH

【讨论】:

    【解决方案2】:
     sed -nr '/^(<[^>]*>).*/{s//\1/;h};/widget/{g;p}' test.xml
    

    打印

    <test>
    <formula>
    

    如果打印您想要的确切格式,则 Sed 仅单行会更复杂。

    编辑:
    您可以在 gnu sed 中使用 /widget/I 而不是 /widget/ 来匹配不区分大小写的 widget,否则就像在其他答案中一样,对每个字母使用 [Ww]

    【讨论】:

      【解决方案3】:

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

      sed '/^<[^/]/!d;:a;/^<\([^>]*>\).*<\/\1/!{$!N;ba};/^<\([^>]*>\).*\(widget\).*<\/\1/s//<\1\2<\/\1/p;d' file
      

      【讨论】:

        【解决方案4】:

        需要gawkRS 中有正则表达式

        BEGIN {
            # make a stream of words
            RS="(\n| )"
        }
        
        # match </tag>
        /<\// {
            s--
            next
        }
        
        # match <tag>
        /</ {
            if (!s) {
            tag=substr($0, 2)
            }
            s++
        }
        
        $0=="widget" {
            print "<" tag $0 "</" tag
        }
        

        【讨论】:

          猜你喜欢
          • 2017-06-12
          • 2014-05-08
          • 1970-01-01
          • 2018-04-05
          • 1970-01-01
          • 2014-07-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多