88

我在 bash 脚本中有同样简单的函数,我想将标准输出作为输入传递给它。

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`

像这样。

4

8 回答 8

83

要回答您的实际问题,当 shell 函数位于管道的接收端时,标准输入会被函数中的所有命令继承,但只有实际从其标准输入读取的命令才会消耗任何数据。对于一个接一个运行的命令,后面的命令只能看到前面的命令没有消耗的内容。当两个命令并行运行时,哪些命令查看哪些数据取决于操作系统如何调度命令。

由于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由一对以空格分隔的数字组成,shell 对 的值执行分词songtime,并jc_hms看到两个单独的参数。这不需要更改 的定义jc_hms,也不需要通过标准输入将任何内容输入其中。

如果您仍然有其他阅读标准输入的理由jc_hms,请告诉我们。

于 2012-07-12T17:20:04.333 回答
62

您不能将内容直接通过管道传输到这样的 bash 函数,但是您可以使用read将其拉入:

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

应该是你想要的

于 2012-07-12T14:49:19.377 回答
57

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

2)我喜欢这里的大部分答案

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

使用 read 内置强制此函数与管道输入一起使用,因此它不能再以典型方式使用。我认为利用 /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

我希望这可以帮助某人。

快乐黑客!

于 2016-02-19T18:44:27.750 回答
26

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

jc_hms() {
    cat
}

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

于 2012-07-12T15:26:00.413 回答
17

我喜欢 user.friendly 使用Bash内置条件未设置替换语法的答案。这里有一个细微的调整,使他的答案更通用,例如参数计数不确定的情况:

function myfunc() {
    declare MY_INPUT=${*:-$(</dev/stdin)}
    for PARAM in $MY_INPUT; do
        # do what needs to be done on each input value
    done
}
于 2016-04-05T17:16:52.637 回答
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,它是最好的而且也是免费的;这将适用于不一定使用最新 gawk 的常见 linux 发行版。最新版本的 gawk 允许您将 UTC 指定为 strftime() 函数的第三个参数。

于 2013-04-19T17:12:48.703 回答
1

提议的解决方案要求内容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对我不起作用。
您可能必须为超时使用不同的值。太小的值可能会导致错误,而太大的超时会延迟脚本。

于 2020-05-05T10:34:40.187 回答
0

似乎没什么用,但有解决方法

提到了围绕 xargs ref 函数的工作

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

那么这也不起作用,因为您的函数可以引用另一个函数。所以我最终编写了一些接受管道输入的函数,如下所示:

somefunc() {
    while read -r data; do
        printf "%s" "$data"
    done
}
于 2021-11-04T03:02:40.637 回答