Shell – Program to filter lines by passing them to an external program

command linefiltershell

I'm looking for a program to use an external program to filter the lines of a stream – pretty much a version of grep that, for each line, prints or suppresses it based on whether the specified program exits with a zero exit code (like find's -exec option).

I know that I can do this in shell by using a loop and a subshell:

some-program |(while read line; do
    if predicate "$line"; then
        echo "$line"
    fi
done)

What I'm wondering is if there's a program floating around that will let me make this simpler:

some-program |filter predicate
# want negation as well
some-program |filter ! predicate

One would think that an enhanced Sed might support this, asking "does it pass on the pattern space?", but GNU Sed does not seem to have such a facility.

Is there such a program somewhere that I haven't found, or do I just need to do it in shell (or perl)?

Best Answer

The shell is a perfectly suitable tool for this job. Just take care not to mangle spaces and backslashes.

while IFS= read -r line; do
  if predicate "$line"; then printf '%s\n' "$line"; done
done

You could also use awk. Be sure to quote each line since it'll be passed to a shell (the snippet below puts single quotes around the line, and replaces single quotes in the line by '\''; \047 is '). Because each command invocation goes through a shell, I expect this to be slower than the pure shell method, even if awk is likely to be faster at parsing lines. But I haven't made any benchmarks.

awk '{quoted=$0; gsub(/\047/, "\047\\\\\047\047")}
     !system("predicate \047" $0 "\047")'
Related Question