如何将输出重定向到 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,因此它必须作为变量传入。该命令输出修改后的内容,通常会被重定向到一个文件或通过管道传输到另一个命令。 (例如sed
、awk
、perl
等)
read
获取标准输入并将其放入变量$string
没有必要通过括号将read
和mystic_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中运行时,这很有用。谢谢! 我希望更多的人了解<(…)
的用法,这简直太棒了。 OP应该给出解释。基本上,它运行一个子进程,但不是通过管道传输它,而是返回一个文件描述符。这允许您在需要文件的地方使用命令。例如(以 root 身份)vimdiff /etc/sysconfig/iptables <(iptables-save)
最终打开一个类似于/proc/23565/fd/63
的文件名。是的,vim
可以接受-
作为文件名并从stdin
读取,但这只是一个示例。可能事情不使用-
,或者使用<()
允许的命令顺序更易读。
@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>&1)
@Kayvar 当你使用$( ... )
时它使用新的子shell,因此命令的结果可能与主shell 不同
如果您明确说明这是否仅捕获 STDOUT 或者它是否还捕获 STDERR,将会对其他人有所帮助。如果您可以展示如何在$(...)
中打印命令的退出代码,那也将非常有帮助以上是关于如何将输出重定向到 shell 中的变量? [复制]的主要内容,如果未能解决你的问题,请参考以下文章