【问题标题】:Pipe output to bash function管道输出到 bash 函数
【发布时间】:2012-07-12 08:25:57
【问题描述】:

我在 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`

像这样。

【问题讨论】:

  • 您的问题是“$1”是函数的命令行参数,而不是标准输入,这是从管道中找到文本的位置。
  • 那么我将如何访问标准输入。例子?
  • 我认为这可能是XY problem。您能否更新您的问题,告诉我们您真正想要实现的目标,而不是您如何尝试实现它?
  • jliu83:如果jc_hms 在管道的接收端,stdin 将呈现给函数内的第一个命令。但是,请发布 jc_hms 真正的样子,以便我们确定适合您情况的最佳解决方案。
  • 请注意你的函数是错误的。它应该真的像jc_hms() { hr=$(($1 / 3600)); min=$((($1 % 3600) / 60)); sec=$((1 % 60)); printf "$hs:%02d:%02d" $min $sec; }

标签: linux bash pipe


【解决方案1】:

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

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

应该是你想要的

【讨论】:

  • 你能举例说明如何使用它吗?我仍然需要以同样的方式使用它吗?
  • 您可以使用这个或我的答案,就像您在问题中表示的那样。
  • 它会按照您的建议工作。如果我用 echo "manipulated $data" 替换 printf 并运行 var=$(echo "teststring" | jc_hms); echo $var 从命令行我得到“操纵的测试字符串”。编辑为 $(..) 因为反引号不会出现在 cmets 中,但您的原始作业应该可以工作
  • 这对我有用。有没有什么方法可以一次性读取所有数据?
  • 它应该是printf "%s" "$data" 否则$data 将被插入为格式字符串。
【解决方案2】:

或者,你也可以用简单的方法来做。

jc_hms() {
    cat
}

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

【讨论】:

  • 谢谢你;现在我知道我可以用它来管道到printf,例如xdotool search --onlyvisible --name 'Audacity' | printf "0x%08x\n" `cat`
  • 这个方法其实可以用,参考我下面的回答:# echo 12345-1234 | printf 'Zip: %s\n' $(</dev/stdin)Zip: 12345-1234# echo 1 2 3 4 | printf 'Number: %d\n' $(</dev/stdin)Number: 1Number: 1Number: 2Number: 3Number: 4
  • @TrueFalse:是的,这行得通,但它使用 shell 将标准输入转换回printf 的参数。 printf 从不读取标准输入,没有必要通过管道连接它。 (你做的和printf 'Number: %d\n' $(echo 1 2 3 4)一样)
  • @Jo So:确实如此,对于 OP 的问题也可以这样说。以我在这里使用的方式使用它是没有意义的,但它展示了该方法。你说你不能通过管道进入 printf;这不是真的,所以我只是为那些非常希望通过管道连接到 printf 的人提出一个论据。
  • @TrueFalse:你不能通过管道进入 printf。您通过管道进入<,它是cat 的缩写,并让shell 执行命令替换(将cat 的输出作为printf 的参数)。
【解决方案3】:

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

由于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 读取标准输入,请告诉我们。

【讨论】:

  • 我遇到了与read 相关的问题,试试这个:echo -e "\ta" |if read str;then echo "$str";fi,它会忽略标签并只打印“a”,有什么提示吗?
  • 在这里找到了修复:stackoverflow.com/questions/7314044/…
  • echo 比 printf 更有说服力,不是吗?
  • echo 从未完全标准化,因为已经存在太多不同(在某些情况下是矛盾的)实现。 (例如,POSIX 标准是让echo 处理像\t\n 这样的序列,除非您使用非标准选项@,否则bash 的实现不会这样做987654341@.)'printf 更便携。
  • “标准输入由函数内部执行的第一个命令读取”——这不是真的。相反,任何从标准输入读取的命令都会这样做,假设在它已经使用流之前没有命令。你可以有 500 行“printf”,然后,在函数的最后一行,cat——函数会打印出你的 500 行,然后cat 会读取 stdin 的内容并将它们写入标准输出。
【解决方案4】:

嗯嗯……

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() 函数的第三个参数。

【讨论】:

    【解决方案5】:

    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
    

    我希望这可以帮助某人。

    黑客愉快!

    【讨论】:

    • 非常有帮助 - 我正好有这个需要,能够在函数中处理 args 或 stdin。
    • 非常好!但似乎是一种 bashism(在 BASH 下工作,但不适用于 DASH)我把它放在一个文件中:#!/bin/sh jc_hms() { i="${1:-$(/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: 算术表达式:期望初级:“/3600”
    • 绝对黄金,谢谢!在安装了 bash 4.4 的 MacOS 上,我无法使用 declare -i,但我只是完成了我的任务:i=${1:-$(&lt;/dev/stdin)};
    • 唯一的问题是它使用${1},如果直接调用它不接受多个参数。例如jc_hms 7800 4321 将完全忽略 4321 输入,而 echo 7800 4321 | jc_hms 按预期工作
    【解决方案6】:

    我喜欢 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
    }
    

    【讨论】:

    • 您能解释一下${*:-$(&lt;/dev/stdin)} 的具体工作原理吗?
    【解决方案7】:

    建议的解决方案要求仅有条件地调用 stdinread 上的内容。否则,该函数将等待来自控制台的内容,并在继续之前需要 EnterCtrl+D

    一种解决方法是使用 read 并设置超时。例如read -t &lt;seconds&gt;

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

    注意,-t 0 对我不起作用。
    您可能必须为超时使用不同的值。 值太小可能会导致错误,而超时会导致脚本延迟。

    【讨论】:

    • 这似乎运作良好,但假设是单行。但是,如果需要多行输入,这可行:while read -t 0.001 -r data; do...
    【解决方案8】:

    似乎没什么用,但有变通办法

    提到work around xargs ref function

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

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

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

    【讨论】:

      猜你喜欢
      • 2019-02-16
      • 1970-01-01
      • 1970-01-01
      • 2015-10-03
      • 2019-08-09
      • 2015-12-07
      • 2013-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多