How to conditionally add a pipeline element in bash

416 Views Asked by At

I need to form a pipeline of various commands. Some elements of the pipeline, or sequences of elements, are only relevant when some condition holds. Now, I could write:

if [[ $whatever ]]; then
   cmd1 | cmd2 | cmd3 | cmd4
else
   cmd1 | cmd4
fi

but that means repeating cmd1 and cmd4, plus, there may be several conditions and I don't want to write nested if's. So, I tried writing this:

if [[ $whatever ]]; then
    pipeline_segment="| cmd2 | cmd3"
else
    pipeline_segment=""
fi

cmd1 ${pipeline_segment} | cmd4

but - the pipe symbol was not interpreted as an instruction to use a pipe.

How do I have bash execute the pipeline I want it too?

Note: You may assume a bash version of 4 or higher, but only if you must.

2

There are 2 best solutions below

6
On

Your attempt fails because special symbols lose their syntactical value after expansion. eval is a way, but as we know it is prone to code injection so it is best avoided. We can try

cmd1 |
if [ condition ]; then
   cmd2 | cmd3
else
   cat
fi | 
cmd4

Though I'd love to skip cat, I couldn't find a way. Real example:

echo XYZABC |
if true; then
   tr Y B | tr Z C
else
   cat
fi | 
tr X A

outputs ABCABC and changing the condition to false outputs AYZABC.

0
On

I presume you're not wanting to repeat anything because they are big gnarly commands. In that case, use functions to encapsulate them:

cmd1() { some big long command with many options and arguments; }
cmd2() { similar; }
... and so on

The you can literally use this

if [[ $whatever ]]; then
   cmd1 | cmd2 | cmd3 | cmd4
else
   cmd1 | cmd4
fi

or, push the whatever condition into the functions:

cmd2 () { 
    if [[ whatever ]]; then
        the big long command
    else
        cat
    fi
}
cmd3() {
    if [[ whatever ]]; then
        the other big long command
    else
        cat
    fi
}

Then, you're left with simply

cmd1 | cmd2 | cmd3 | cmd4