Conditional pipeline
Just the usual &&
and ||
operators:
cmd1 < input.txt |
cmd2 |
( [ -n "$DEFINED" ] && cmd3 || cat ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
Although, as specified by this answer, you would generally prefer if ... else
, if you're after the if-else syntax:
...
cmd2 |
if [ -n "$DEFINED" ]; then cmd3; else cat; fi |
cmd4 |
...
(Note that no trailing backslash is needed when the line ends with pipe.)
Update according to Jonas' observation.
If cmd3 may terminate with non-zero exit code and you not want cat
to process the remaining input, reverse the logic:
cmd1 < input.txt |
cmd2 |
( [ -z "$DEFINED" ] && cat || cmd3 ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
if
/else
/fi
works. Assuming any Bourne-like shell:
cmd1 < input.txt |
cmd2 |
if [ -n "$DEFINED" ]; then cmd3; else cat; fi |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
All the answers given so far replace cmd3
with cat
. You can also avoid running any command with:
if [ -n "$DEFINE" ]; then
alias maybe_cmd3='cmd3 |'
else
alias maybe_cmd3=''
fi
cmd1 |
cmd2 |
maybe_cmd3
cmd4 |
... |
cmdN > result.txt
That's POSIX, but note that if in a bash
script where bash
is not in sh
-mode (like with a script starting with #! /path/to/bash
), you'll need to enable alias expansion with shopt -s expand_aliases
(or set -o posix
).
Another approach that still doesn't run any unnecessary command is to use eval:
if [ -n "$DEFINE" ]; then
maybe_cmd3='cmd3 |'
else
maybe_cmd3=''
fi
eval "
cmd1 |
cmd2 |
$maybe_cmd3
cmd4 |
... |
cmdN > result.txt"
Or:
eval "
cmd1 |
cmd2 |
${DEFINE:+cmd3 |}
cmd4 |
... |
cmdN > result.txt"
On Linux (at least), instead of cat
, you could use pv -q
which uses splice()
instead of read()
+ write()
to pass the data across between the two pipes which avoids having the data moved twice between kernel and user space.