Bash function that accepts input from parameter or pipe

See Stéphane Chazelas's answer for a better solution.


You can use /dev/stdin to read from standard input

b64decode()
{
    if (( $# == 0 )) ; then
        base64 --decode < /dev/stdin
        echo
    else
        base64 --decode <<< "$1"
        echo
    fi
}
  • $# == 0 checks if number of command line arguments is zero
  • base64 --decode <<< "$1" one can also use herestring instead of using echo and piping to base64

Here it should just be:

b64decode() {
  if [ "$#" -gt 0 ]; then
    # concatenated arguments fed via a pipe.
    printf %s "$@" | base64 --decode
  else
    base64 --decode  # read from stdin
  fi
  ret=$?
  echo # add one newline character
  return "$ret" # return with base64's exit status to report decoding
                # errors if any.
}

In any case, do not use base64 --decode < /dev/stdin. At best (on most systems) < /dev/stdin does nothing, it just does the equivalent of dup2(0,0) (duplicating fd 0 onto fd 0, a no-op).

But on Linux or Cygwin, < /dev/stdin does not work properly here. On those systems, opening /dev/stdin is not like duplicating stdin, it reopens from scratch and independently the same file as is currently opened on stdin.

So if previously stdin was pointing in the middle of some regular file, after < /dev/stdin, you'll end up with stdin now pointing at the start of that file. If stdin was the writing end of a pipe (which it shouldn't under normal circumstances), you'll end up with it being the reading end. If it was a socket, then it will fail as sockets cannot be opened. Same if you don't have permission to open that file for reading (for instance because the permissions changed or the file was originally opened on stdin using different credentials).

And after base64 --decode < /dev/stdin returns, the current position of stdin within the file (for seekable file input) will have been left untouched, not left at the end (or wherever base64 stopped reading) since base64's stdin was on a different open file description.


Sundeep's answer works for base64 because that utility does not support multiple lines. A more general fix for the more general case

  • target utility allowing multiple commandline arguments
  • pipe or redirect containing multiple lines

is something like

my_function() {
    if (( ${#} == 0 )) ; then
        while read -r line ; do
            target_utility "${line}"
        done
    else
        target_utility "${@}"
    fi
}