管道输出到bash功能


71

我在bash脚本中具有简单的功能,我想将stdout作为输入传递给它。

jc_hms(){
  printf "$1"
}

我想以这种方式使用它。

var=`echo "teststring" | jc_hms`

当然,我使用了冗余函数echo和printf来简化问题,但是您明白了。现在,我收到一个“未找到”错误,我认为这意味着我的参数定界是错误的(“ $ 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 totaltime”的字符串,由空格分隔。

我希望我可以在一行中执行此操作,并在awk之后将其发送

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

像这样


2
您的问题是“ $ 1”是函数的命令行参数,而不是标准输入,这是从管道中找到文本的位置。
chepner

1
那么我将如何访问标准输入。例?
jliu83

2
我认为这可能是XY问题。您能否更新您的问题以告诉我们您真正想要实现的目标,而不是告诉我们如何实现?
ghoti 2012年

jliu83:如果jc_hms在管道的接收端,则将stdin呈现给函数内的第一个命令。但是,请发布jc_hms的实际外观,以便我们确定适合您情况的最佳解决方案。
chepner

请注意,您的功能有误。它应该真的像jc_hms() { hr=$(($1 / 3600)); min=$((($1 % 3600) / 60)); sec=$((1 % 60)); printf "$hs:%02d:%02d" $min $sec; }
Jo So

Answers:


69

为了回答您的实际问题,当外壳函数位于管道的接收端时,标准输入由该函数内部执行的第一个命令读取。由于这printf是函数中的第一个也是唯一的命令,因此将忽略标准输入。有几种解决方法,包括使用read内置方法将标准输入读入变量,该变量可以传递给printf

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

但是,由于您对管道的需求似乎取决于您对使用的感知需求awk,所以让我建议以下替代方法:

printstring=$( jc_hms $songtime )

由于songtime由一对以空格分隔的数字组成,因此外壳程序对的值进行单词拆分songtime,并jc_hms看到两个单独的参数。这不需要更改的定义jc_hms,也不需要通过标准输入将任何内容输入其中。

如果您还有其他原因jc_hms需要阅读标准输入,请告诉我们。


我有一个相关的问题read,请尝试以下操作:echo -e "\ta" |if read str;then echo "$str";fi,它将忽略选项卡并仅打印“ a”,有任何提示吗?
Aquarius Power


echo比printf更简洁,不是吗?
桑德堡

2
echo从来没有完全标准化,因为已经存在太多不同的实现(在某些情况下甚至是相互矛盾的)实现。(例如,POSIX标准是echo像过程顺序\t\n,它bash的实现也不会,除非你使用非标准的选项做-e。)“printf是更为便携。
chepner '19

53

您不能将内容直接通过管道传递给bash函数,但是可以使用read它来拉入:

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

应该是你想要的


1
您可以举一个使用它的例子吗?我仍然必须以相同的方式使用它吗?
jliu83

您可以使用此答案,也可以使用我的答案,就像您在问题中指出的那样。
chepner

它会按照您的建议工作。如果我将printf替换为echo“ manipulated $ data”并运行var = $(echo“ teststring” | jc_hms); 从命令行回显$ var我得到“操纵的测试字符串”。编辑为$(..),因为反引号不会出现在注释中,但您的原始作业应该可以工作
moopet 2012年

这为我做到了。有一种方法可以用一根衬套一次读取所有数据吗?
Avindra Goolcharan 2014年

4
应该为printf "%s" "$data",否则$data将作为格式字符串输入。
拉斐尔·阿伦斯

47

1)我知道这是一个很老的帖子

2)我喜欢这里的大多数答案

但是,我找到了这篇文章是因为我需要类似的东西。尽管每个人都同意需要使用stdin,但这里缺少的答案是/ dev / stdin文件的实际用法。

使用内置读取功能时,该功能将与管道输入一起使用,因此无法再以典型方式使用。我认为利用/ dev / stdin是解决此问题的一种更好的方法,因此为了完整性我想加2美分。

我的解决方案:

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;
}

实际上:

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

我希望这可以帮助某人。

骇客入侵!


2
非常有用-我正是有这个需要,以便能够在函数中处理args或stdin。
darrend

很好!但似乎是一种bashism(在BASH下工作,但不能在DASH下工作)我将其放在文件中:#!/ bin / sh jc_hms(){i =“ $ {1:-$(</ dev / stdin)}” printf“%02d:%02d:02d \ n” $(($ i / 3600))$(($ i / 60%60))$(($ i%60))} jc_hms 7800 echo 7800 | jc_hms然后chmod + x test.sh ./test.sh 02:10:00 ./test.sh:6:./test.sh:算术表达式:期望主
变量

1
绝对的金,谢谢!在安装了bash 4.4的MacOS上,我无法使用declare -i,但只完成了我的任务:i=${1:-$(</dev/stdin)};
ptim

20

或者,您也可以通过简单的方式进行操作。

jc_hms() {
    cat
}

尽管到目前为止所有答案都忽略了这不是OP想要的事实(他说功能得到了简化)


谢谢你 现在我知道我可以使用它来进行管道printfxdotool search --onlyvisible --name 'Audacity' | printf "0x%08x\n" `cat`
传输

1
您实际上可以使用此方法,请参阅以下我的答案: # echo 12345-1234 | printf 'Zip: %s\n' $(</dev/stdin) Zip: 12345-1234 # echo 1 2 3 4 | printf 'Number: %d\n' $(</dev/stdin) Number: 1 Number: 2 Number: 3 Number: 4
user.friendly

@TrueFalse:是的,可以,但是它使用外壳程序从stdin转换回的参数printfprintf从不读取stdin,没有指向它的管道。(您的操作与相同printf 'Number: %d\n' $(echo 1 2 3 4)
Jo So

@Jo So:是的,关于OP的问题也可以这样说。以我在这里使用的方式使用它是没有意义的,但这证明了该方法。您说不能通过管道传递给printf;那不是真的,所以我只是为那些非常希望使用printf的人争论。
user.friendly

@TrueFalse:您不能通过管道传递给printf。您将管道输送到<,这是的缩写cat,然后让外壳执行命令替换(将catas的输出放入printf)。
乔苏

15

我喜欢使用Bash内置的条件未设置替换语法的user.friendly答案。为了使他的答案更加通用,需要做一些细微的调整,例如对于参数计数不确定的情况:

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

1

嗯...

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

如果仍然要调用awk,为什么不使用它呢?

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

我假设您使用的是Gnu的Awk,这是最好的,也是免费的;这可以在不一定使用最新版本的普通Linux发行版中使用。使用最新版本的gawk,您可以将UTC指定为strftime()函数的第三个参数。


0

提出的解决方案要求仅在条件上调用内容stdinread仅对其有条件地调用。否则,该功能将等待控制台中的内容,并且需要输入EnterCtrl+D才能继续。

一种解决方法是read与超时一起使用。例如read -t <seconds>

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

注意,-t 0对我没有用。
您可能必须为超时使用其他值。值太小可能会导致错误,并且太大的超时会延迟脚本。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.