【问题标题】:Sed/awk - How to remove newline characters between start pattern and end pattern.Sed/awk - 如何删除开始模式和结束模式之间的换行符。
【发布时间】:2018-06-30 05:43:28
【问题描述】:

示例日志:

2018-01-01 11:30:22 xxx Parsing xxx
2018-01-01 11:30:23 driver queryId=<xxx> Parsing command: select *
from table 
limit 10
2018-01-01 11:30:25 Parsing completed 
2018-01-01 11:30:28 xxxxxx
2018-01-01 11:30:40 driver queryId=<xxx> Parsing command: select * from table group by column
2018-01-01 11:30:45 Parsing completed 
2018-01-01 11:30:51 xxxxxx
2018-01-01 11:30:52 xxx Parsing xxx
2018-01-01 11:30:54 driver queryId=<xxx> Parsing command: select 

*
from table 

order by column

limit 20
2018-01-01 11:30:56 Parsing completed 
2018-01-01 11:30:59 xxxxxx

我想删除“Parsing command:”和“2018”匹配模式之间的换行符,输出应包含仅匹配该模式的单词。

解析示例:

2018-01-01 11:30:54 driver queryId=<xxx> Parsing command: select 

*
from table 

order by column

limit 20
2018-01-01 11:30:56 Parsing completed

上面例子的输出应该是,

select * from table order by column limit 20

【问题讨论】:

  • 应该只处理有换行符的查询吗?

标签: linux awk sed


【解决方案1】:

这是一个使用 perl 而不是 sed/awk 的非常简短的解决方案:

perl -ne 's/\n/ /; print +(s/^.*Parsing command: // .. /^2018/ or next) =~ /E/ ? "\n" : $_' input.log

想法:

我们遍历输入行 (-n)。对于每一行我们执行代码(-e ...):

  • 首先,我们将换行符替换为空格 (s/\n/ /)。
  • 然后我们检查COND1 .. COND2 条件,该条件适用于 COND1 和 COND2 范围内的所有行。
  • 我们的第一个条件是替换s/^.*Parsing command: //,如果它设法删除了以Parsing command: 结尾的输入行的某些前缀,则为真。这是我们范围的开始。
  • 我们的第二个条件是匹配/^2018/,如果输入行以2018 开头,则为真。这是我们范围的尽头。
  • 如果此检查失败,我们将跳到下一个输入行 (... or next)。对于其余代码,我们只考虑范围内的行。
  • .. 返回的值是一个序列号。该范围的最后一行附加了E0。我们检查/E/ 以排除范围的最后一行(以2018 开头的行),因为我们不想打印它。
  • 如果我们在最后一行,我们只输出一个换行符 ("\n"),否则我们打印该行(最后的换行符从第一次替换转换为空格)。

【讨论】:

  • @melpomene 很好,但是您可以考虑将模式 Parsing completed 作为您的第二个条件,我觉得它更权威。无论如何,很好的解释:+)
【解决方案2】:

sed也可以用,虽然看起来有点吓人:-/

sed -nE '/Parsing command:/{
s/^.*Parsing command://;:l1;N;/Parsing completed[[:blank:]]*$/!bl1;
s/2018-.*Parsing completed[[:blank:]]*$//;
s/\n/ /g;s/^[[:blank:]]*//;s/[[:blank:]]+/ /gp}' logfile

注意最后两个替换用于一些细粒度的格式,最后一个sp 标志负责打印。


输出

select * from table limit 10 
select * from table group by column 
select * from table order by column limit 20 

一切顺利:-)


推荐阅读: sed branching 声明。

【讨论】:

    【解决方案3】:

    Awk解决方案:

    awk '/Parsing command:/{ f=1; sub(/.*Parsing command: /,""); q=$0; next }
         f && /^2018/{ gsub(/[[:space:]]{2,}/, " ", q); print q; f=0 }
         NF && f{ q=q" "$0 }' logfile
    

    输出:

    select * from table limit 10
    select * from table group by column
    select * from table order by column limit 20
    

    【讨论】:

    • 可能是另外一个sed 以抑制多余的空格。 :-)
    • @sjsam, Awk 有足够的力量自己达到))完成
    • 格式正确!你可以考虑/Parsing completed[[:blank:]]*$/而不是^2018,因为前者更权威。不过只是一个建议。
    【解决方案4】:

    保持简单。鉴于您发布的第一个输入文件,使用 GNU awk 进行多字符 RS 和 RT:

    $ awk -F'Parsing command: ' -v RS='[^\n]+Parsing completed' 'RT{gsub(/\s+/," ",$NF); print $NF}' file
    select * from table limit 10
    select * from table group by column
    select * from table order by column limit 20
    

    或使用任何 awk:

    $ cat tst.awk
    /Parsing completed/ {
        gsub(/ +/," ",buf)
        sub(/.*Parsing command: /,"",buf)
        print buf
        buf = ""
    }
    { buf = buf " " $0 }
    
    $ awk -f tst.awk file
    select * from table limit 10
    select * from table group by column
    select * from table order by column limit 20
    

    【讨论】:

      【解决方案5】:

      sed 脚本:文件extractcommand.sed

      #!/usr/bin/sed -f
      /Parsing command:/!{d;b}          # delete+continue if 'Parsing command' not found
      :a                                # if found, then start a loop with label (a)
        s/.*Parsing command:\s*//       #   delete that 'Parsing command'
        /Parsing completed/{            #   if found 'Parsing completed'
          s:\n[^\n]*Parsing completed:: #     then delete that 'Parsing completed'
          s:\n: :g                      #     change all \n to space
          s:  *: :g                     #     remove all extra spaces (optional)
          b                             #     break the loop (and print as default)
        }                               #
        N                               #   load another line into buffer
        ba                              #   loop to label (a)
      

      测试:

      $ ./extractcommand.sed <sample.log 
      select * from table limit 10 
      select * from table group by column 
      select * from table order by column limit 20
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-16
        • 2019-08-31
        • 1970-01-01
        • 1970-01-01
        • 2013-10-21
        • 2022-01-21
        • 1970-01-01
        相关资源
        最近更新 更多