如何从文件中删除一组行?

如何从文件中删除一组行?

我正在编写一个 ksh 脚本来解析日志文件并在发现重要消息时发送电子邮件。有些消息是信息性的,我想忽略它们。

日志文件有格式

2018-01-24.08.24.35.875675    some text

    more text
    more text
    more text
    more text

2018-01-24.08.24.37.164538    some text

    more text
    more text
    INF9999W        <-- informational text
    more text

2018-01-24.08.24.46.8602545    some text

    more text
    more text
    more text

时间戳将被视为消息分隔符,时间戳属于其后的消息。我想在文件中搜索每次出现的“信息文本”,然后从文件中删除整个消息(从前一个时间戳到下一个时间戳之前)。

如何轻松确定前后时间戳的行号,以便我使用以下命令删除这些行:

awk 'NR<'$preceding_ts' || NR >='$following_ts'

我的方法是将所有时间戳行放入一个文件中,然后循环该文件,直到找到“信息文本”行 # 之前和之后的时间戳行。看起来工作量很大,尤其是在处理大文件时。有没有更有效的方法。

integer inf_line
integer last_ts_line
integer cur_ts
cp $error_log $copy_log
while true
do
   inf_line=$(grep -n "INF99999W" $copy_log | head -1 | cut -f1 -d":")
   if [[ $inf_line -eq 0 ]]
   then
      break
   fi     
   grep -n -E "^20[0-9][0-9]-[0-1][0-9]-[0-3][0-9]-" $copy_log | cut -f1 -d":" > $ts_lines
   last_ts_line=99999999
   cat $ts_lines | while read cur_ts
   do       
      if [[ $cur_ts -gt $inf_line && $last_ts_line -lt $inf_line ]]
      then
         awk 'NR<'$last_ts_line' || NR >='$cur_ts'' $copy_log > $temp_log
         cp $temp_log $copy_log
         last_ts_line=$cur_ts
         break
      fi
      last_ts_line=$cur_ts
   done
   if [[ $last_ts_line -lt $inf_line ]]
   then
      awk 'NR<'$last_ts_line'' $copy_log > $temp_log
      cp $temp_log $copy_log
   fi
done

谢谢。

答案1

我会通过存储当前消息的行来实现它,当消息结束时,如果没有INF看到标记,则打印存储的批次。这里,d保存当前消息的行(d 代表数据),p告诉我们是否要打印存储的行。

awk -vinfo='INF99+' \
    '/^20[0-9][0-9]-[0-1][0-9]-[0-3][0-9]/ {
         if (p) printf "%s", d; d = $0 ORS; p=1; next } 
     $0 ~ info {p=0} 
     {d = d $0 ORS} 
     END {if (p) printf "%s", d}' < log 

这里的第一条规则匹配时间戳行,如果p为 true,则打印任何存储的行,存储该行并设置p为 1。如果看到p具有该模式的线,则第二条规则将重置为零;info模式设置为变量-vinfo=...。第三条规则将当前行附加到收集的行中,并且该END规则再次仅打印收集的行(如果p设置)。


我们也可以这样写,这也会检查info时间戳行上的模式:

awk -vinfo='INF99+' \
    '/^20[0-9][0-9]-[0-1][0-9]-[0-3][0-9]/ {
         if (p) { printf "%s", d }; d = ""; p=1; } 
     $0 ~ info {p=0} 
     {d = d $0 ORS} 
     END {if (p) printf "%s", d}' < log 

一般来说,用awkPerl 或 Perl 编写这样的东西可能是个好主意。结果至少比 fork grepawkcut等的数十个副本的 shell 脚本运行得快得多......

相关内容