Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Is there a way to pipe the output of one function to another, but only if there is an output?

share|improve this question
add comment

6 Answers

up vote 4 down vote accepted

The following ifnotempty function pipes its input to the command passed as an argument, except that it does nothing if the input is empty. Use it to pipe source --foo into sink --bar by writing source --foo | pipe_if_not_empty sink --bar.

pipe_if_not_empty () {
  head=$(dd bs=1 count=1 2>/dev/null; echo a)
  head=${head%a}
  if [ "x$head" != x"" ]; then
    { printf %s "$head"; cat; } | "$@"
  fi
}

Design notes:

  • I would expect this implementation to work on all POSIX/Unix platforms, though strictly speaking it is not standards-compliant: it relies on dd not reading more than the one byte it's told to read on its standard input.
  • I think head -c 1 would be a suitable replacement for dd bs=1 count=1 2>/dev/null on Linux.
  • On the other hand, head -n 1 would not be suitable because head typically buffers its input and may read more than the one line it outputs — and since it's reading from a pipe, the extra bytes are just lost.
  • read -r head and even read -r -n 1 head are not suitable here because if the first character is a newline, head would be set to the empty string, making it impossible to distinguish between empty input and input starting with a blank line.
  • We can't just write head=$(head -c 1) because if the first character is a newline, command substitution would strip the final newline, making it impossible to distinguish between empty input and input starting with a blank line.
  • In bash, ksh or zsh, you can replace cat by </dev/stdin for a microscopic performance gain.

If you don't mind storing the whole intermediate data in memory, here is a very slightly simpler implementation of pipe_if_not_empty.

pipe_if_not_empty () {
  input=$(cat; echo a);
  if [ "x$input" != x"a" ]; then
    { printf %s "${input%a}"; } | "$@"
  fi
}

Here is a slightly simpler implementation with the following caveats:

  • The data produced by the source is considered empty if and only if it consists solely of newline characters. (This may in fact be desirable.)
  • The data fed into the sink ends with exactly one newline character, no matter how many newlines the data produced by the source ends with. (This is could be a problem.)

Again, the whole data is stored in memory.

pipe_if_not_empty () {
  input=$(cat);
  if [ "x$input" != x"" ]; then
    { printf '%s\n' "${input}"; } | "$@"
  fi
}
share|improve this answer
add comment

ifne(1) from moreutils does exactly that. Moreutils is available as a package at least in Debian and Ubuntu, probably in other distros as well.

share|improve this answer
add comment

The function below tries to read the 1st byte, and if successfull echos that byte and cats the rest. Should be efficient and 100% portable.

if_read() {
    IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | eval $@;
}

Test cases:

$ echo -n | if_read wc -c
$ echo | if_read wc -c
1
$ echo -en "\nX" | if_read wc -c
2
$
share|improve this answer
add comment

Instead of sender | receiver:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester

Or you could make it more general purpose by changing it to accept the receiving program as an argument as in Gilles' answer:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver
share|improve this answer
add comment

At least something like this works:

yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi

Please note that the above will consider line feeds and other special characters as output, so an empty line passed to that if statement will be considered as an output. Just raise the -gt limit if your output should usually be higher than 1 byte :)

share|improve this answer
    
yourothercommand never sees the output of yourcommand. –  Dennis Williamson Nov 12 '10 at 12:00
    
Whoops! That's right! Downvote me like there's no tomorrow. –  Janne Pikkarainen Nov 12 '10 at 12:03
3  
▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼ –  Dennis Williamson Nov 12 '10 at 12:11
    
Thanks, I needed that. –  Janne Pikkarainen Nov 12 '10 at 12:15
add comment

This should work for you

$ --a function-- | [ xargs -r ] --another function--

An example

$ echo -e "\n\n" | xargs -r ls
$ # No output. ls did not run.
$ echo -e "\n\n1" | xargs -r ls
ls: cannot access 1: No such file or directory

It's simple but it should work for you. If your "a function" sends an empty string or even a newline down the pipeline, xargs -r, will prevent passage through to "another function".

Reference for xargs: http://www.oreillynet.com/linux/cmd/cmd.csp?path=x/xargs

-r, --no-run-if-empty
Do not run command if standard input contains only blanks.
share|improve this answer
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.