JBT
JBT

Reputation: 8746

How to filter lines with a filter chain

Suppose you have a text file, say list.txt, like this:

# Category 1
foobar

# Category 2
dummy1
dummy2
dummy3

# Category 3
foobar.dummy
foobar.dummy

And you have a bash script, say, list.sh, to extract lines from list.txt. The script takes one or more patterns to filter the text file with grep. Conceptually, it will be something like this from commandline:

cat list.txt | grep filter1 | grep fitler1 | ... | grep filtern

However, the problem is that the number of filters varies, so that you have to use a loop to do the filtering. For the loop, I am hoping something like below would work.

filters=$*
for filter in ${filters[@]}; do
    result=`ad_hoc_show $result | grep $filter`
done

ad_hoc_show $result # should maintain original line structure

For example, below is desired output.

$ list.sh foobar
foobar
foobar.dummy
foobar.dummy

$ list.sh dummy \d
dummy1
dummy2
dummy3

So, any advice on how to implement the ad_hoc_show function?

Upvotes: 2

Views: 293

Answers (2)

Etan Reisner
Etan Reisner

Reputation: 80921

Something like this should work:

#!/bin/sh

ad_hoc_show() {
    filter=$1
    shift

    if [ $# -eq 0 ]; then
        grep "$filter"
        return
    fi

    grep "$filter" | ad_hoc_show "$@"
}

file=$1
shift
ad_hoc_show "$@" <"$file"

As David C. Rankin points out in his comment:

For bash it is possible to avoid unnecessary sub-shells by replacing

grep "$filter" | ad_hoc_show "$@"

with

ad_hoc_show "$@" <<<"$(grep "$filter")"

in the recursive call.

And you could avoid using shift by using offset array indexing (I don't know if there's an official term for this) by using "${@:2}" in thead_hoc_showcalls and removing theshift` lines.

Upvotes: 1

anubhava
anubhava

Reputation: 784918

If your grep supports -P then you can use this function:

filt() {
   re=$(printf "(?=.*?%s)" "$@")
   grep -P "$re" list.txt
}

filt 'dummy' '\d'
dummy1
dummy2
dummy3

filt 'foobar'
foobar
foobar.dummy
foobar.dummy

UPDATE: In case grep -P is not available then you can use awk:

filt() { re=$(printf "/%s/&&" "$@"); awk "${re:0: -2}" list.txt; }

filt 'dummy' '[0-9]'
dummy1
dummy2
dummy3

filt 'foobar'
foobar
foobar.dummy
foobar.dummy

Upvotes: 2

Related Questions