如何将输出重定向到 shell 中的变量? [复制]

Posted

技术标签:

【中文标题】如何将输出重定向到 shell 中的变量? [复制]【英文标题】:How do I redirect output to a variable in shell? [duplicate] 【发布时间】: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 的所有功能。 【参考方案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

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

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

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

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

    没有必要通过括号将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 )在***.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”混淆。

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

http://www.linuxjournal.com/content/shell-process-redirection http://tldp.org/LDP/abs/html/process-sub.html http://tldp.org/LDP/abs/html/commandsub.html☚ 对比一下

【讨论】:

这需要更多的支持。这些天来唯一对我有用的解决方案。 当子命令不能在子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://***.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,将会对其他人有所帮助。如果您可以展示如何在$(...) 中打印命令的退出代码,那也将非常有帮助

以上是关于如何将输出重定向到 shell 中的变量? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何将 dos 命令的输出读取/重定向到 C/C++ 中的程序变量?

可以通过 shell 重定向捕获彩色输出吗? [复制]

将输出重定向到for循环shell中特定目录中的多个文件

shell中的重定向(输入输出)

shell脚本中的重定向符号

怎样去掉提示:nohup:重定向标准错误到标准输出