如何在Ruby中使用带有unix管道的$ stdout和$ stdin?

时间:2014-10-31 15:39:19

标签: ruby awk pipe stdout stdin

我可以像这样创建一个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语句被忽略(但由于某种原因{ {1}}命令仍返回两行):

head

我确信有更好的方法可以做到这一点(转义awk '{print toupper($1)}' #<IO:0x007fe18406ac58> | awk '{ print tolower($1)}' 引用?)。这是我能想到的最简单的可重复的例子。在我的真实脚本中,我允许多个输入源(标准输入或文件参数)。不可协商的要求是STDIN代码需要对输入的引用,我不能在Ruby中逐行处理它。

有什么想法吗?

更新 按照@ tadman的建议,我已经完成了这个并且它有效!:

awk

有没有办法重构这个?

2 个答案:

答案 0 :(得分:1)

这里的问题是您直接提供$stdin而不是用它做任何事情。这就是Ruby将其渲染为原始对象的原因。它是一个文件句柄,除非你对它执行方法,而不是原始数据。

您想要的是从该文件句柄中获取所有内容:

$stdin.read

如果您要使用Ruby,则没有理由使用awk

#!/usr/bin/env ruby

puts STDIN.read.upcase

如果您希望将换行符保留为"\n",请执行以下操作:

puts STDIN.read.upcase.inspect

如果您已承诺使用外部命令:

require 'open3'

Open3.popen3("awk '{print toupper($1)}'") do |cmd_in, cmd_out, cmd_err|
  # Read from our STDIN and push through to the command's STDIN
  cmd_in.write(STDIN.read)

  # Close STDIN on the command to tell it we're finished writing.
  cmd_in.close

  # Read result from command's STDOUT and write to our STDOUT
  puts cmd_out.read.inspect
end

答案 1 :(得分:0)

好的,我找到了一种更简单的方式

require "open3"
Open3.pipeline(
    ["awk '{print toupper($1)}'"],
    ["awk '{print tolower($1)}'"], 
:in => STDIN) # this is redundant, but I might want to change :in in the future