1

I have very complicated set of commands:

command | ... | ... | tee >(grep -c '[^3]$') >(grep -c '[^35]$') 1>/dev/null

I don't want to have a temporary file to save up output, as it is pretty huge. I tried doing >(grep -c '[^3]$' | read variable) and >(grep -c '[^3]$' | read variable2) but I guess bit doesn't work because of process substitution's sub-shell invocation.

What can I do to pipe output directly to multiple variables? Is it even possible?

Right now I have this workaround:

var=$(command ... | ... | ... | tee >(grep -c '[^3]$') >(grep -c '[^35]$') 1>/dev/null)
var1=$(tail -1 <<< $var)
var2=$(head -1 <<< $var)

but I think it is clumsy and doesn't look nice. I am aware I can grep into another file but I don't like it either.

Thanks in advance.

1
  • have you tried using awk instead of grep? an awk script could easily increment two separate counters for the two different patterns, and (in the END block) output the counts to two different files (or to one file if that's more useful to you).
    – cas
    Commented May 13, 2021 at 14:32

2 Answers 2

4

Use awk, instead of tee-ing to two grep process substitutions. For example:

command | ... | ... | awk '
     /[^3]$/  { c1++; next };
     /[^35]$/ { c2++; next };
     END { print c1, c2 > counts.txt }'

read var1 var2 < counts.txt
rm counts.txt

If you don't want to use a temporary file, you could do it like this:

read var1 var2 < <(command | ... | ... | awk '
         /[^3]$/  { c1++; next };
         /[^35]$/ { c2++; next };
         END { print c1, c2 }')
4
  • I am sure, but that part is irrelevant. This was just a random shell script where I realized piping into two variables might be very useful. I just used it as an example.
    – Fedja
    Commented May 13, 2021 at 14:42
  • you forgot writing awk to a file
    – Fedja
    Commented May 13, 2021 at 14:44
  • 1
    @FedjaMladenovic yeah, my mouse wheel is wonky and randomly pastes stuff when i use it. accidentally deleted the > counts.txt from the awk END block. I need a new mouse.
    – cas
    Commented May 13, 2021 at 14:46
  • yeah no problem I know it's a typo
    – Fedja
    Commented May 13, 2021 at 14:47
0

Firstly, you're sending standard output in your command chain to /dev/null, so youre assignation will likely be assigning a null value. Remove that. It looks like you're trying to capture only the first and last line of the output; this can be tricky to do in a one-liner but awk should be able to handle it:

var="$(command ... | [...] | tee [...] | awk 'NR==1{print $0} END { print $0}')"

However, pipes are simple creatures. They have one input and one output. You can't (easily) use pipes to send data into multiple places. The closest you'll be able to get would be to use named pipes, which essentially are the temporary files you're trying to avoid (technically not true; FIFO queues are different from files, but I am simplifying).

2
  • About the /dev/null, maybe I don't get you, but I do it to "filter out" the output. So only stdout ( redirected to var) I get are from 2 greps, and non from tee. I try to capture only the first and last line of output only because output is 2 greps, so one line is one grep and second line is other grep.
    – Fedja
    Commented May 13, 2021 at 14:37
  • That wouldn't be necessary if you were just piping into grep (commands | grep -cE '[^3]$|[^35]$') (though of course the output would be commingled. For parsing the same input data set into different result sets, I'd be inclined to use awk and several output files (or FIFOs).
    – DopeGhoti
    Commented May 13, 2021 at 14:55

You must log in to answer this question.

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