Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Say I've got the following pipeline:

cmd1 < input.txt |\
cmd2 |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt

Under certain conditions I would like to add a cmd3 between cmd2 and cmd4. Is there a way to create a kind conditional pipeline without saving the result of cmd2 into a temporary file ? I would think of something like:

cmd1 < input.txt |\
cmd2 |\
(${DEFINED}? cmd3 : cat ) |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt
share|improve this question
add comment

6 Answers

up vote 16 down vote accepted

Just the usual && and || operators:

cmd1 < input.txt |
cmd2 |
( [[ "${DEFINED}" ]] && cmd3 || cat ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

(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 |
( [[ ! "${DEFINED}" ]] && cat || cmd3 ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
share|improve this answer
    
Handy tip Noted! –  invert May 10 '12 at 11:08
2  
Be aware of the gotchas: unix.stackexchange.com/a/39043/19055 –  Jonas Kölker May 20 '12 at 11:09
add comment

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
share|improve this answer
add comment

As an addendum to manatwork's accepted answer: be aware of the and-false-or gotcha and its interaction with streams. For instance,

true && false || echo foo

outputs foo. Not surprisingly,

true && (echo foo | grep bar) || echo baz

and

echo foo | (true && grep bar || echo baz)

both output baz. (Note that echo foo | grep bar is false and has no output). However,

echo foo | (true && grep bar || sed -e abaz)

outputs nothing. This may or may not be what you want.

share|improve this answer
    
I can't see how is this relevant here. The else (or ||) should act as a null operation keeping the input unchanged. So replacing cat with echo changes everything making your code irrelevant. Regarding the “and-false-or gotcha”, I see no interference with the pipeline: pastebin.com/u6Jq0bZe –  manatwork May 20 '12 at 11:29
2  
@manatwork: One thing Jonas is pointing out is that, in [[ "${DEFINED}" ]] && cmd3 || cat, cmd3 and cat are not the mutually exclusive then and else branches of the same if, but that if cmd3 fails, then cat will also be executed. (Of course, if cmd3 always succeeds, or if it consumes all the input so there's nothing left in stdin for cat to process, then the it may not matter.) If you need both then and else branches, it's better to use an explicit if command, not && and ||. –  musiphil Dec 2 '12 at 21:17
1  
Thank you for the explanation, @musiphil. Now I understand Jonas' argument. –  manatwork Dec 3 '12 at 7:10
add comment

I had a similar situation that I solved with bash functions:

if ...; then
  my_cmd3() { cmd3; }
else
  my_cmd3() { cat; }
if

cmd1 < input.txt |
cmd2 |
my_cmd3 |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
share|improve this answer
add comment

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

Though not POSIX (because POSIX shells are not required to expand aliases in non-interactive shells), that would work in all of ash, pdksh, ksh, zsh and also bash, but for bash you need shopt -s expand_aliases so virtually all POSIX shells.

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"
share|improve this answer
add comment

Another option, that does not require a cat:

if [[ "${DEFINED}" ]]; then
  MAYBE_CMD3="cmd3 |"
else
  MAYBE_CMD3=
fi
cmd1 < input.txt |
cmd2 |
$MAYBE_CMD3
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

If the condition does not hold, MAYBE_CMD3 will just be empty and it's as if you didn't write it.

share|improve this answer
    
Nope, won't work, $MAYBE_CMD3 is interpreted as a simple command whose arguments are the result of the word splitting of $MAYBE_CMD3 (with globbing applied on top). So it will call cmd3 with cmd3 and | as the arguments. –  Stephane Chazelas Feb 5 '13 at 22:01
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.