tail -f | awk and end tail once data is found

2019-07-27 16:40发布

问题:

I am trying to build a script which tail -f | awk the log file which is getting updated every second. awk part will fetch me only the required part of the log file based on my search parameter. Output XML is also captured in an output file. Script is working fine - as expected.

Issue - However ever after the search is performed it stays hung due to tail -f. Any idea how to update below script - so that once the output XML is captured, it should break the tail part??

XMLF=/appl/logs/abc.log

aa_pam=${1-xml}
[[ ${2-xml} = "xml" ]] && tof=xml_$(date +%Y%m%d%H%M%S).xml || tof=$2
tail -f $XMLF | \
awk  ' BEGIN { Print_SW=0; Cnt_line=1; i=0}
       /\<\?xml version\=/ { if (Print_SW==1) p_out(Cnt_Line,i)
                             Print_SW=0; Cnt_line=1;
       }
       { Trap_arry[Cnt_line++]=$0;
       }
       /'${1-xml}'/ { Print_SW=1;
       }
       /\<\/XYZ_999/    { if (Print_SW==1) p_out(Cnt_Line, i);
                             Print_SW=0; Cnt_line=1; }
       END { if (Print_SW==1) p_out(Cnt_Line, i); }
function p_out(Cnt_Line, i) {
       for (i=1;i<Cnt_line;i++) {print Trap_arry[i] | "tee '$tof'" }
}
' | tee $tof

Update Tried as per below suggestion of using exit - it is existing the script successfully - however the xml that is getting captured at output is getting duplicated. So in the output file - same XML appears twice..!!

XMLF=/appl/logs/abc.log

aa_pam=${1-xml}
[[ ${2-xml} = "xml" ]] && tof=xml_$(date +%Y%m%d%H%M%S).xml || tof=$2
tail -f $XMLF | \
awk  ' BEGIN { Print_SW=0; Cnt_line=1; i=0}
       /\<\?xml version\=/ { if (Print_SW==1) p_out(Cnt_Line,i)
                             Print_SW=0; Cnt_line=1;
       }
       { Trap_arry[Cnt_line++]=$0;
       }
       /'${1-xml}'/ { Print_SW=1;
       }
       /\<\/XYZ_999/    { if (Print_SW==1) p_out(Cnt_Line, i);
                             Print_SW=0; Cnt_line=1; }
       END { if (Print_SW==1) p_out(Cnt_Line, i); }
function p_out(Cnt_Line, i) {
       for (i=1;i<Cnt_line;i++) {print Trap_arry[i] | "tee '$tof'" } { exit }
}
' | tee $tof

回答1:

Based on this question How to break a tail -f command in bash you could try

#! /bin/bash

XMLF=/appl/logs/abc.log

aa_pam=${1-xml}
[[ ${2-xml} = "xml" ]] && tof=xml_$(date +%Y%m%d%H%M%S).xml || tof=$2

mkfifo log.pipe
tail -f "$XMLF" > log.pipe & tail_pid=$!

awk  -vpar1="$aa_pam" -vtof="$tof" -f t.awk  < log.pipe
kill $tail_pid
rm log.pipe

where t.awk is:

/<\?xml version\=/ {
    if (Print_SW==1) {
        p_out(Cnt_Line)
    }
    Print_SW=0
    Cnt_line=0
}

{
    Trap_arry[++Cnt_line]=$0
}

$0 ~ par1 {
    Print_SW=1;
}

/<\/XYZ_999/    {
    if (Print_SW==1)
        p_out(Cnt_Line)
    Print_SW=0
    Cnt_line=0
}

function p_out(Cnt_Line, i) {
    for (i=1; i<Cnt_line; i++) {
        print Trap_arry[i] | ("tee " tof)
    }
    exit 1
}


回答2:

Call exit (which will jump to your END block prior to termination) after you are finished capturing your output.

When awk terminates, the next write() to stdout by tail -f will result in an EPIPE error. tail knows to terminate when that happens.

UPDATE: You seem to be having some problem trying to decide where to put the exit. It should not be in p_out because you call p_out from both the closing XML tag match expression and from the END block. Try this instead:

XMLF=/appl/logs/abc.log

aa_pam=${1-xml}
[[ ${2-xml} = "xml" ]] && tof=xml_$(date +%Y%m%d%H%M%S).xml || tof=$2
tail -f $XMLF | \
awk  '
  BEGIN {
    Print_SW=0
    Cnt_line=1
    i=0
  }

  /\<\?xml version\=/ {
     if (Print_SW==1)
        p_out(Cnt_Line,i)
     Print_SW=0
     Cnt_line=1
  }

  {
    Trap_arry[Cnt_line++]=$0
  }

  /'${1-xml}'/ {
    Print_SW=1;
  }

  /\<\/XYZ_999/ {
    if (Print_SW==1)
      p_out(Cnt_Line, i)
    Print_SW=0
    Cnt_line=1
    exit
  }

  END {
    if (Print_SW==1)
      p_out(Cnt_Line, i);
  }

  function p_out(Cnt_Line, i) {
    for (i=1;i<Cnt_line;i++) {
      print Trap_arry[i] | "tee '$tof'"
    }
  }
' | tee $tof


回答3:

You could, in the awk script, add a line such as:

/some-end-of-xml-marker/  {  close(/dev/stdin) ; }

I didn't try it, but you get the idea: close STDIN when you reached the end of the file, so that the loop in awk stops and you get to the END part (not tested, I hope this proves to be correct...)



标签: awk pipe tail