【发布时间】:2014-10-31 15:39:18
【问题描述】:
我可以像这样创建一个 ruby 可执行文件(虚拟示例):
echo '#!/usr/bin/env ruby
puts %x[awk ''{print toupper($1)}'' #{STDIN} ]
' > pipes.rb
chmod +x pipes.rb
然后我可以使用这个文件其他 unix 工具:
echo "a\nb\nc\nd" | ./pipes.rb | head -n2
# A
# B
但如果我需要通过另一个对 awk 的调用来扩展虚拟示例,则它不起作用:
echo '#!/usr/bin/env ruby
puts %x[awk ''{print toupper($1)}'' #{STDIN} | awk ''{ \
print tolower($1) \
}'']
' >! pipes2.rb
chmod +x pipes2.rb
echo "a\nb\nc\nd" | ./pipes2.rb | head -n2
# A
# B
# it should be: "a\nb"
问题是STDIN 被字符串化为:#<IO:0x007fe18406ac58> 并且哈希被解释为注释,因此第二个 awk 语句被忽略(但由于某种原因,head 命令仍然返回两个行):
awk '{print toupper($1)}' #<IO:0x007fe18406ac58> | awk '{ print tolower($1)}'
我确信有更好的方法来做到这一点(转义 STDIN 参考?)。这是我能想到的最简单的可重现示例。在我的真实脚本中,我允许多个输入源(标准输入或文件参数)。不可协商的要求是 awk 代码需要对输入的引用,我不能在 Ruby 中逐行处理它。
有什么想法吗?
更新 按照@tadman 的建议,我已经做到了,并且成功了!:
#!/usr/bin/env ruby
require "open3"
Open3.popen3("awk '{print toupper($1)}'") do |cmd_in, cmd_out, cmd_err|
cmd_in.write(STDIN.read)
cmd_in.close
Open3.popen3("awk '{print tolower($1)}'") do |cmd_in2, cmd_out2, cmd_err2|
cmd_in2.write(cmd_out.read)
cmd_in2.close
puts cmd_out2.read
end
end
echo "aAA\nbBB\ncCC\ndDD" | ./pipes2.rb | head -n2
# aaa
# bbb
有没有办法重构这个?
【问题讨论】:
-
在生产 Ruby 代码中看到
$stdin和$stdout有点不寻常,它们是受 Perl 启发的全局变量。大多数时候你会看到STDIN和STDOUT被使用。STDOUT.puts也是多余的,因为默认情况下puts会转到STDOUT。 -
好的,我可以使用
STDIN和puts,但是结果是一样的。
标签: ruby awk pipe stdout stdin