管道输出到 bash 功能

我在 bash 脚本中有一个简单的函数,我希望将 stdout 作为输入输出到它。

jc_hms(){
printf "$1"
}

我想用这种方式使用它。

var=`echo "teststring" | jc_hms`

当然,我使用了冗余函数 echo 和 printf 来简化这个问题,但是您已经明白我的意思了。现在我得到了一个“ not found”错误,我假设这意味着我的参数划分是错误的(“ $1”部分)。有什么建议吗?

最初 jc _ hms 函数是这样使用的:

echo `jc_hms "teststring"` > //dev/tts/0

但是我想先将结果存储在一个变量中,以便进一步处理,然后再将其发送到串行端口。

编辑: 因此,澄清一下,我不是要打印东西到串行端口,我想接口到我的 bash 函数应“ |”管道字符,我想知道这是否可能。

编辑: 好的,这是完整的功能。

jc_hms(){
hr=$(($1 / 3600))
min=$(($1 / 60))
sec=$(($1 % 60))


printf "$hs:%02d:%02d" $min $sec
}

我用这个函数来形成一个字符串,这行代码就是这个字符串

songplaytime=`echo $songtime | awk '{print S1 }'`
printstring="`jc_hms $songplaytime`"  #store resulting string in printstring

其中 $Songtime 是一个字符串,表示为由空格分隔的“ playtime total time”。

我希望我可以只用一句话做到这一点,然后在结束之后用笛子吹奏

printstring=`echo $songtime | awk '{print S1 }' | jc_hms`

像这样。

91457 次浏览

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

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)

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.

Hmmmm....

songplaytime=`echo $songtime | awk '{print S1 }'`
printstring="`jc_hms $songplaytime`"  #store resulting string in printstring

if you're calling awk anyway, why not use it?

printstring=`TZ=UTC gawk -vT=$songplaytime 'BEGIN{print strftime("%T",T)}'`

I'm assuming you're using Gnu's Awk, which is the best one and also free; this will work in common linux distros which aren't necessarily using the most recent gawk. The most recent versions of gawk will let you specify UTC as a third parameter to the strftime() function.

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!

I like user.friendly's answer using the Bash built-in conditional unset substitution syntax. Here's a slight tweak to make his answer more generic, such as for cases with an indeterminate parameter count:

function myfunc() {
declare MY_INPUT=${*:-$(</dev/stdin)}
for PARAM in $MY_INPUT; do
# do what needs to be done on each input value
done
}

The proposed solutions require content on stdin or read to be only conditionally called. Otherwise the function will wait for content from the console and require an Enter or Ctrl+D before continuing.

A workaround is to use read with a timeout. e.g. read -t <seconds>

function test ()
{
# ...
# process any parameters
# ...
read -t 0.001 piped
if [[ "${piped:-}" ]]; then
echo $piped
fi
}

Note, -t 0 did not work for me.
You might have to use a different value for the time-out. Too small a value might result in bugs and a too large time-out delays the script.

seems nothing works, but there are work arounds

mentioned work around xargs ref function

$ FUNCS=$(functions hi); seq 3 | xargs -I{} zsh -c "eval $FUNCS; hi {}"

then this doesn't work either because your function could reference another function. so I ended up writing some function that accepts pipe inputs, like this:

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