Pipe output to bash function

You can't pipe stuff directly to a bash function like that, however you can use read to pull it in instead:

jc_hms() {
  while read -r data; do
      printf "%s" "$data"
  done
}

should be what you want


To answer your actual question, when a shell function is on the receiving end of a pipe, standard input is inherited by all commands in the function, but only commands that actually read form their standard input consume any data. For commands that run one after the other, later commands can only see what isn't consumed by previous commands. When two commands run in parallel, which commands see which data depends on how the OS schedules the commands.

Since printf is the first and only command in your function, standard input is effectively ignored. There are several ways around that, including using the read built-in to read standard input into a variable which can be passed to printf:

jc_hms () {
    read foo
    hr=$(($foo / 3600))
    min=$(($foo / 60))
    sec=$(($foo % 60))
    printf "%d:%02d:%02d" "$hr" "$min" "$sec"
}

However, since your need for a pipeline seems to depend on your perceived need to use awk, let me suggest the following alternative:

printstring=$( jc_hms $songtime )

Since songtime consists of a space-separated pair of numbers, the shell performs word-splitting on the value of songtime, and jc_hms sees two separate parameters. This requires no change in the definition of jc_hms, and no need to pipe anything into it via standard input.

If you still have a different reason for jc_hms to read standard input, please let us know.


1) I know this is a pretty old post

2) I like most of the answers here

However, I found this post because I needed to something similar. While everyone agrees stdin is what needs to be used, what the answers here are missing is the actual usage of the /dev/stdin file.

Using the read builtin forces this function to be used with piped input, so it can no longer be used in a typical way. I think utilizing /dev/stdin is a superior way of solving this problem, so I wanted to add my 2 cents for completeness.

My solution:

jc_hms() { 
  declare -i i=${1:-$(</dev/stdin)};
  declare hr=$(($i/3600)) min=$(($i/60%60)) sec=$(($i%60));
  printf "%02d:%02d:%02d\n" $hr $min $sec;
}

In action:

user@hostname:pwd$ jc_hms 7800
02:10:00
user@hostname:pwd$ echo 7800 | jc_hms 
02:10:00

I hope this may help someone.

Happy hacking!


Or, you can also do it in a simple way.

jc_hms() {
    cat
}

Though all answers so far have disregarded the fact that this was not what OP wanted (he stated the function is simplified)

Tags:

Linux

Bash

Pipe