0

Consider the following command block:

if [[ ${EXTRA_PROCESS} -eq 1 ]]; then
  producer | tee >(calc1 > ${out1}) >(calc2 > ${out2}) | consumer
else
  producer | tee >(calc1 > ${out1}) | consumer
fi

This does what it's supposed to do. However, that's rather a lot of code repartition. So I changed it to look like this:

TEE="tee >(calc1 > ${out1})"
if [[ ${EXTRA_PROCESS} -eq 1 ]]; then
  TEE="$TEE >(calc2 > ${out2})"
fi
producer | ${TEE} | consumer

That's much less code duplication. Unfortunately, the script is now completely broken. When I run it, it creates three giant files in the current directory:

blackbox:~ # ls
>
>(calc1
>(calc2
...

(I'm impressed that it's even possible to have files with those names in the first place... Deleting them was even more fun!)

Can somebody explain

  • Why Bash is treating the strings literally rather than interpreting them?
  • How I can force Bash to actually do what I asked for?
MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220
  • possible duplicate of [How can I expand arguments to a bash function into a chain of piped commands?](http://stackoverflow.com/questions/12334429/how-can-i-expand-arguments-to-a-bash-function-into-a-chain-of-piped-commands) – chepner Dec 02 '13 at 12:28

2 Answers2

1

Why Bash is treating the strings literally rather than interpreting them?

When you store the redirection operators in strings, those tend to lose their special meaning. This is the reason that you're able to have filenames like >(calc1.

How I can force Bash to actually do what I asked for?

I'd suggest sticking to the way you've done it in your first code block. It might appear to duplicate code but an alternative (see below) isn't really recommended.


Alternate (not recommended):

eval "producer | ${TEE} | consumer"

A better alternative would be to use functions:

foo1() {
  producer | tee >(calc1 > ${out1}) >(calc2 > ${out2}) | consumer;
}

foo2() {
  producer | tee >(calc1 > ${out1}) | consumer;
}

if [[ ${EXTRA_PROCESS} -eq 1 ]]; then
  foo1
else
  foo2
fi
devnull
  • 118,548
  • 33
  • 236
  • 227
  • The real script has _multiple_ settable options, resulting in a combinatorial explosion of nearly identical but subtly different commands in a deeply-nested if-block... I'd really like something that humans can actually read and understand. – MathematicalOrchid Dec 02 '13 at 11:28
  • @MathematicalOrchid Not much can be said without looking at the script, but for the example you've mentioned consider using functions. – devnull Dec 02 '13 at 11:31
  • Wrapping each duplicate copy of the pipeline in a function doesn't really do anything about the actual duplication... unless there's some way to use a function as just one part of a pipeline? Because with 4 on/off options, that's 2^4 = 16 nearly identical pipelines I need to edit (_and test!_) – MathematicalOrchid Dec 02 '13 at 11:38
  • @MathematicalOrchid Does that leave you with `eval`? – devnull Dec 02 '13 at 11:41
0

I found a solution:

if [[ ${EXTRA_PROCESS} -eq 1 ]]; then
  function Tee()
  {
    tee >(calc1 > ${out1}) >(calc2 > ${out2})
  }
else
  function Tee()
  {
    tee >(calc1 > ${out1})
  }
fi

producer | Tee | consumer

For the example actually shown, this is quite a bit more typing. However, when you have multiple independent on/off options, doing it this way prevents a combinatorial explosion of slightly different pipelines.

producer | Option1 | Option2 | Option3 | consumer
MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220