【问题标题】:How do I redirect output to a variable in shell? [duplicate]如何将输出重定向到 shell 中的变量? [复制]
【发布时间】:2011-02-03 06:48:15
【问题描述】:

我有一个这样的脚本

genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5

我想在一个变量中获取由 genhash 生成的流。如何将其重定向到变量 $hash 以在条件内进行比较?

if [ $hash -ne 0 ]
  then echo KO
  exit 0
else echo -n OK
  exit 0
fi

【问题讨论】:

  • 这个问题比链接的答案更老。因此链接的答案是重复的,应该标记为“之前询问过”,而不是这个。
  • Bash 和 Shell 是不同的东西,因此没有重复。 Shell 不支持 bash 的所有功能。

标签: bash shell


【解决方案1】:

TL;DR

"abc" 存储到$foo

echo "abc" | read foo

但是,由于管道会创建分叉,因此您必须在管道结束之前使用$foo,所以...

echo "abc" | ( read foo; date +"I received $foo on %D"; )

当然,所有这些其他答案都显示了不按 OP 要求做的方法,但这真的把我们其他搜索 OP 问题的人搞砸了。

问题的答案是使用read命令。

这是你的做法

# I would usually do this on one line, but for readability...
series | of | commands \
| \
(
  read string;
  mystic_command --opt "$string" /path/to/file
) \
| \
handle_mystified_file

这是它正在做什么以及为什么它很重要:

  1. 让我们假设series | of | commands 是一系列非常复杂的管道命令。

  2. mystic_command 可以接受文件内容作为标准输入来代替文件路径,但不是--opt arg,因此它必须作为变量传入。该命令输出修改后的内容,通常会被重定向到一个文件或通过管道传输到另一个命令。 (例如sedawkperl 等)

  3. read 获取标准输入并将其放入变量$string

  4. 没有必要通过括号将readmystic_command 放入“子shell”,但它会像连续管道一样流动,就像两个命令在单独的脚本文件中一样。

总有一个替代方案,在这种情况下,与我上面的示例相比,替代方案是丑陋且不可读的。

# my example above as a oneliner
series | of | commands | (read string; mystic_command --opt "$string" /path/to/file) | handle_mystified_file

# ugly and unreadable alternative
mystic_command --opt "$(series | of | commands)" /path/to/file | handle_mystified_file

我的方式完全是按时间顺序和合乎逻辑的。替代方案从第 4 个命令开始,并将命令 1、2 和 3 推入命令替换。

我在 this script 中有一个真实世界的示例,但我没有将它用作上面的示例,因为它还有一些其他疯狂/令人困惑/分散注意力的 bash 魔术。

【讨论】:

  • 我也相信这是正确的答案。有些情况下 $(...) 不起作用,而到处都接受管道。
  • @RichardBronosky 感谢您发布此答案。可以访问父进程中的变量(例如,在管道中更远的地方,比如handle_mystified_file 部分)?这似乎是不可能的,因为read 在子进程中运行,不会影响父进程中的变量。我正在尝试使用tee 拆分进程的输出,稍后通过读取一个进程的输出作为另一个参数来“重新连接”tee-divided 进程。
  • @PhươngNguyễn,这行不通,因为管道会创建进程分叉。这是我不得不添加括号的另一个原因。 echo "test" | (read ROLE_X; echo $ROLE_X )stackoverflow.com/a/13764018/117471阅读更多关于这个分叉的信息
  • 仍然不能使用括号。 ls | (read $x; echo $x) 仅打印 ls 输出的第一行。 'read' 不能读取多于一行的输出。
  • 如果你的标准输入有多行,你可以使用read -d '' string
【解决方案2】:

创建一个函数,将其作为您要调用的命令来调用。在这种情况下,我需要使用 ruok 命令。

然后,调用该函数并将其结果分配给一个变量。在这种情况下,我将结果分配给变量 health

function ruok {
  echo ruok | nc *ip* 2181
}

health=echo ruok *ip*

【讨论】:

    【解决方案3】:
    read hash < <(genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5)
    

    此技术使用 Bash 的“process substitution”,不要与“command substitution”混淆。

    这里有一些很好的参考资料:

    【讨论】:

    • 这需要更多的支持。这些天来唯一对我有用的解决方案。
    • 当子命令不能在子shell中运行时,这很有用。谢谢!
    • 我希望更多的人了解&lt;(…) 的用法,这简直太棒了。 OP应该给出解释。基本上,它运行一个子进程,但不是通过管道传输它,而是返回一个文件描述符。这允许您在需要文件的地方使用命令。例如(以 root 身份)vimdiff /etc/sysconfig/iptables &lt;(iptables-save) 最终打开一个类似于/proc/23565/fd/63 的文件名。是的,vim 可以接受- 作为文件名并从stdin 读取,但这只是一个示例。可能事情不使用-,或者使用&lt;() 允许的命令顺序更易读。
    • @IntrepidDude 我编辑了答案以包含您(和其他人)需要知道的内容。谢谢你的提问。这将使所有人受益。
    • 看起来这只捕获了 STDOUT 而不是 STDERR
    【解决方案4】:

    如果管道太复杂而无法用$(...) 包装,请考虑编写一个函数。可以访问在定义时可用的任何局部变量。

    function getHash {
      genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5
    }
    hash=$(getHash)
    

    http://www.gnu.org/software/bash/manual/bashref.html#Shell-Functions

    【讨论】:

    • 您甚至可能想将一个复杂的管道写入自己的 shell 脚本中,并将其命名为 hash=$(getHash.sh) -- shell 脚本将在新终端中可用,甚至在重新启动后也是如此。跨度>
    【解决方案5】:

    使用$(`code`) 构造函数时有时会出错。

    最后我在这里找到了一些方法:https://stackoverflow.com/a/7902174/2480481

    基本上,使用 Tee 再次读取输出并将其放入变量中。 这就是您如何查看正常输出然后从输出中读取它的方式。

    不是吗?我猜你当前的任务 genhash 只会输出一个字符串哈希,所以可能对你有用。

    我是新手,仍在寻找完整的输出并保存到 1 个命令中。 问候。

    【讨论】:

      【解决方案6】:

      我猜兼容方式:

      hash=`genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5`
      

      但我更喜欢

      hash="$(genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5)"
      

      【讨论】:

      • 感谢您提及兼容性。那是对我的环境有效的方法,而其他方法却没有。
      • $(...) 周围不需要引号。
      • 是的,有需要。例如。如果输出中有 *,取决于您以后如何处理它,它会尝试将其扩展为一个 glob。
      • @Koshinae,它将尝试扩展echo $hash。但是,如果您这样做,* 将保持原样hash=$(echo '*'0); echo "$hash"
      • 如果结果是多个单词,它会创建一个数组吗?因此,通过用引号括起来它会创建一个字符串而不是字符串数组。不太确定,对 bash 来说相对较新
      【解决方案7】:

      你可以这样做:

      hash=$(genhash --use-ssl -s $IP -p 443 --url $URL)
      

      hash=`genhash --use-ssl -s $IP -p 443 --url $URL`
      

      如果你想将整个管道的结果赋值给变量,可以在上面的赋值中使用整个管道。

      【讨论】:

        【解决方案8】:

        使用$( ... ) 构造:

        hash=$(genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5)
        

        【讨论】:

        • 这会捕获命令的输出,但它不使用重定向。如果您因为更复杂的需要而确实需要使用重定向,请参阅我的答案。谷歌把你带到了这里,对吧?为什么要去其他地方找到您搜索的答案?
        • 其实不是 OP 问题的答案
        • @BrunoBronosky 这对我有用:result=$(( $your_command ) 2&gt;&amp;1)
        • @Kayvar 当你使用$( ... ) 时它使用新的子shell,因此命令的结果可能与主shell 不同
        • 如果您明确说明这是否仅捕获 STDOUT 或者它是否还捕获 STDERR,将会对其他人有所帮助。如果您可以展示如何在$(...) 中打印命令的退出代码,那也将非常有帮助
        猜你喜欢
        • 2019-02-13
        • 1970-01-01
        • 1970-01-01
        • 2014-03-03
        • 2014-10-31
        • 1970-01-01
        • 1970-01-01
        • 2017-06-13
        相关资源
        最近更新 更多