Search code examples
bashshellfilterhighlightinggrep

Speed up bash filter function to run commands consecutively instead of per line


I have written the following filter as a function in my ~/.bash_profile:

hilite() {
 export REGEX_SED=$(echo $1 | sed "s/[|()]/\\\&/g")
 while read line
 do
  echo $line | egrep "$1" | sed "s/$REGEX_SED/\x1b[7m&\x1b[0m/g"
 done
 exit 0
}

to find lines of anything piped into it matching a regular expression, and highlight matches using ANSI escape codes on a VT100-compatible terminal.

For example, the following finds and highlights the strings bin, U or 1 which are whole words in the last 10 lines of /etc/passwd:

tail /etc/passwd | hilite "\b(bin|[U1])\b"

However, the script runs very slowly as each line forks an echo, egrep and sed.

In this case, it would be more efficient to do egrep on the entire input, and then run sed on its output.

How can I modify my function to do this? I would prefer to not create any temporary files if possible.

P.S. Is there another way to find and highlight lines in a similar way?


Solution

  • sed can do a bit of grepping itself: if you give it the -n flag (or #n instruction in a script) it won't echo any output unless asked. So

    while read line
     do
      echo $line | egrep "$1" | sed "s/$REGEX_SED/\x1b[7m&\x1b[0m/g"
     done
    

    could be simplified to

    sed -n "s/$REGEX_SED/\x1b[7m&\x1b[0m/gp"
    

    EDIT: Here's the whole function:

    hilite() { 
        REGEX_SED=$(echo $1 | sed "s/[|()]/\\\&/g");
        sed -n "s/$REGEX_SED/\x1b[7m&\x1b[0m/gp"
    }
    

    That's all there is to it - no while loop, reading, grepping, etc.