0

I have a command that produces a number of output lines that I later grep searching for.

Based on a past answer I know I can use a single long command and do all my filters concurrently:

https://superuser.com/questions/7448/can-the-output-of-one-command-be-piped-to-two-other-commands

command | tee >(grep filter1 >./filter1) >(grep filter2 >./filter2)

Now if only I could create a structure from my filter parameters that I can later decouple into that long command, then I could write a script that does the heavy lifting for me, like in the following shell pseudocode:

filters='filter1 filter2 filter3'
for filter in $filters; do
  SOMEHOW_STORE_REDIRECTIONS+=>(grep $filter > ./${filter})
command | tee >{SOMEHOW_DECOUPLE_STORE_REDIRECTIONS_VAR_INTO_LONG_COMMAND} | cat > /dev/null
2
  • | cat > file could be always replaced with > file. But since it is not related to the main question, I would suggest to remove redirection to /dev/null at all
    – belkka
    Mar 27, 2020 at 15:24
  • Could you provide an example, how do you expect the script could be used? Do you suppose that filter = "grep invocation" or "any shell command"? Note, that in former case each filter definition requires two separate parameters — the grep pattern and the filename to store filtered data, because not all grep patterns could be valid filenames. In your example "filter1", "filter2" and "filter3" are both patterns and filenames at the same time
    – belkka
    Mar 27, 2020 at 15:40

2 Answers 2

0

The easiest way is to do them one at a time recursively:

filter() {
  if [ "$#" -eq 0 ]
  then
    cat
  else
    f="$1"
    shift
    tee >(grep "$f" > "$f") | filter "$@"
  fi
}

printf '%s\n' {foo,bar}{0..100} | filter foo bar r3

However, this adds a number of extra buffers and steps, so alternatively, you can make careful use of eval. This is OK because the filenames are carefully being escaped:

filter() {
   cmd="tee "
   for f in "$@"
   do
     cmd+=">(grep -e ${f@Q} > ${f@Q}) "
   done
   eval "$cmd"
}
0

You can do that with eval:

# usage xfilter filter_spec filter_args...
xfilter() {
    local cmd flt=$1; shift
    printf -v cmd " >($flt)" "$@"
    eval "tee $cmd"
    wait
}

$ echo paa | xfilter 'sed %q' s/a/e/g s/a/o/g
paa
pee
poo

$ tgrep(){ grep "$1" > "./$1"; }
$ printf '%s\n' foo bar | xfilter 'tgrep %q' foo bar
foo
bar
$ grep . foo bar
foo:foo
bar:bar

And no, there's no problem at all with eval if used judiciously. After all, bash will call eval on each line of your script separately, take a second to think about it.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .