【问题标题】:How to make a bash function which can read from standard input?如何制作一个可以从标准输入读取的bash函数?
【发布时间】:2013-09-16 15:49:34
【问题描述】:

我有一些使用参数的脚本,它们工作得很好,但我希望它们能够从标准输入读取,例如从管道读取,例如,假设这称为读取:

#!/bin/bash
function read()
{
 echo $*
}

read $*

现在这适用于read "foo" "bar",但我想将其用作:

echo "foo" | read

我该如何做到这一点?

【问题讨论】:

标签: bash pipe stdin


【解决方案1】:

另一个版本:

  1. 通过管道或参数传递文本来工作
  2. 通过更改最后一行的命令轻松复制和粘贴
  3. 适用于 bash、zsh
# Prints a text in a decorated ballon
function balloon()
{
  (test -p /dev/stdin && cat - || echo $@) figlet -t | cowsay -n -f eyes | toilet -t --gay -f term
}

用法:

# Using with a pipe
$ fortune -s | balloon
# Passing text as parameter
balloon "$(fortune -s )"

【讨论】:

    【解决方案2】:

    在这里聚会迟到了。以@andyanswer 为基础,这是我定义to_uppercase 函数的方式。

    • 如果标准输入不为空,则使用标准输入
    • 如果 stdin 为空,则使用 args
    • 如果 args 为空,则什么也不做
    to_uppercase() {
      local input="$([[ -p /dev/stdin ]] && cat - || echo "$@")"
      [[ -n "$input" ]] && echo "$input" | tr '[:lower:]' '[:upper:]' 
    }
    

    用法:

    $ to_uppercase 
    $ to_uppercase abc
    ABC
    $ echo abc | to_uppercase 
    ABC
    $ to_uppercase <<< echo abc 
    ABC
    
    

    Bash 版本信息:

    $ bash --version
    GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
    

    【讨论】:

      【解决方案3】:

      我发现这可以使用testawk 在一行中完成...

          test -p /dev/stdin  && awk '{print}' /dev/stdin
      

      test -p 测试管道上的输入,该管道通过标准输入接受输入。只有存在输入时,我们才想运行awk,否则它将无限期挂起,等待永远不会到来的输入。

      我已经把它放到一个函数中让它易于使用......

      inputStdin () {
        test -p /dev/stdin  && awk '{print}' /dev/stdin  && return 0
        ### accepts input if any but does not hang waiting for input
        #
        return 1
      }
      

      用法...

      _stdin="$(inputStdin)"
      

      另一个函数使用 awk 未经测试等待命令行输入...

      inputCli () {
        local _input=""
        local _prompt="$1"
        #
        [[ "$_prompt" ]]  && { printf "%s" "$_prompt" > /dev/tty; }
        ### no prompt at all if none supplied
        #
        _input="$(awk 'BEGIN {getline INPUT < "/dev/tty"; print INPUT}')"
        ### accept input (used in place of 'read')
        ###   put in a BEGIN section so will only accept 1 line and exit on ENTER
        ###   WAITS INDEFINITELY FOR INPUT
        #
        [[ "$_input" ]]  && { printf "%s" "$_input"; return 0; }
        #
        return 1
      }
      

      用法...

      _userinput="$(inputCli "Prompt string: ")"
      

      请注意,第一个printf 上的&gt; /dev/tty 似乎是在命令替换$(...) 中调用函数时获得打印提示所必需的。

      awk 的这种使用允许消除用于从键盘或标准输入收集输入的古怪 read 命令。

      【讨论】:

        【解决方案4】:

        将许多其他答案组合成对我有用的答案(这个人为的示例将小写输入变为大写):

          uppercase() {
            local COMMAND='tr [:lower:] [:upper:]'
            if [ -t 0 ]; then
              if [ $# -gt 0 ]; then
                echo "$*" | ${COMMAND}
              fi
            else
              cat - | ${COMMAND} 
            fi
          }
        

        一些例子(第一个没有输入,因此没有输出):

        :; uppercase
        :; uppercase test
        TEST
        :; echo test | uppercase 
        TEST
        :; uppercase <<< test
        TEST
        :; uppercase < <(echo test)
        TEST
        

        一步一步:

        • 测试文件描述符 0 (/dev/stdin) 是否被终端打开

          if [ -t 0 ]; then
          
        • 测试 CLI 调用参数

          if [ $# -gt 0 ]; then
          
        • 将所有 CLI 参数回显到命令

          echo "$*" | ${COMMAND}
          
        • 否则,如果stdin 是通过管道传输的(即不是终端输入),则将stdin 输出到命令(cat -catcat /dev/stdin 的简写)

          else
            cat - | ${COMMAND}
          

        【讨论】:

        • [ -t 0 ] 在某些情况下对我来说失败(例如,从 git-hook 中调用脚本)。我用[ ! -p /dev/stdin ] 替换了它,并且能够让它在更广泛的情况下工作。
        【解决方案5】:

        这里是 bash 中 sprintf 函数的示例实现,它使用 printf 和标准输入:

        sprintf() { local stdin; read -d '' -u 0 stdin; printf "$@" "$stdin"; }
        

        示例用法:

        $ echo bar | sprintf "foo %s"
        foo bar
        

        这会让您了解函数如何从标准输入中读取数据。

        【讨论】:

          【解决方案6】:

          编写一个可以读取标准输入的函数有点棘手,但在没有给出标准输入的情况下可以正常工作。如果您只是尝试从标准输入中读取,它将一直阻塞,直到收到任何内容为止,就像您在提示符处键入 cat 一样。

          在 bash 4 中,您可以通过使用 -t 选项和参数为 0 的 read 来解决此问题。如果有任何可用输入,它会成功,但不会消耗任何输入;否则,它会失败。

          这是一个简单的函数,如果它有来自标准输入的任何内容,则其工作方式类似于 cat,否则工作方式类似于 echo

          catecho () {
              if read -t 0; then
                  cat
              else
                  echo "$*"
              fi
          }
          
          $ catecho command line arguments
          command line arguments
          $ echo "foo bar" | catecho
          foo bar
          

          这使得标准输入优先于命令行参数,即echo foo | catecho bar 将输出foo。要使参数优先于标准输入(echo foo | catecho bar 输出 bar),您可以使用更简单的函数

          catecho () {
              if [ $# -eq 0 ]; then
                  cat
              else
                  echo "$*"
              fi
          }
          

          (它还具有使用 任何 POSIX 兼容 shell 的优势,而不仅仅是 bash 的某些版本)。

          【讨论】:

          • 关于如何在 bash 3 中执行此操作的任何提示?
          • 没什么简单的。我认为您需要尝试从后台的标准输入中读取,如果尚未完成,则在一秒钟后将其杀死,然后尝试确定它是否读取了任何内容。我不确定如何以万无一失的方式做到这一点(子shell中存在变量问题,处理标准输入的部分读取和竞争条件)。
          • @chepner:这适用于哪个版本的 bash?它不适用于 4.1.0。
          • 怎么样?在 4.1.2 中为我工作。
          • 我已经设法让它在我的系统上工作:' if read -t 0.00001 STDIN;然后 echo "$STDIN" ' 工作的最小超时时间似乎至少取决于 CPU 速度,也可能取决于系统负载。 0.01 或 0.001 之类的值可能相当可靠。
          【解决方案7】:

          您可以使用&lt;&lt;&lt; 来获取此行为。 read &lt;&lt;&lt; echo "text" 应该成功了。

          readly测试(我不喜欢使用保留字):

          function readly()
          {
           echo $*
           echo "this was a test"
          }
          
          $ readly <<< echo "hello"
          hello
          this was a test
          

          使用管道,基于this answer to "Bash script, read values from stdin pipe"

          $ echo "hello bye" | { read a; echo $a;  echo "this was a test"; }
          hello bye
          this was a test
          

          【讨论】:

          • 在第一个示例中,您实际上只是将单词“echo”输入readly 的标准输入,它忽略了它的标准输入,并将第一个参数设置为“hello”。您可以将“echo”更改为任何其他单个单词并获得相同的结果。
          • 第二个示例将丢弃其标准输入第一行之后的所有内容。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-09-01
          • 2011-04-14
          • 2020-03-07
          • 2012-12-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多