2014-10-31 36 views
1

我可以创建一个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中逐行处理它。

任何想法?

UPDATE 继@ 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 

有没有办法来重构呢?

+0

这是一个有点不寻常,看看'$ stdin'和'$ stdout'用于生产Ruby代码,它们是受Perl启发的全局变量。大多数时候你会看到使用'STDIN'和'STDOUT'。 'STDOUT.puts'也是多余的,因为默认情况下'puts'转到'STDOUT'。 – tadman 2014-10-31 15:55:27

+0

好吧,我可以使用'STDIN'和'puts',但结果是一样的。 – nachocab 2014-10-31 15:59:23

回答

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 
+0

谢谢,但我确实需要使用awk。 upcase/downcase只是一个例子 – nachocab 2014-10-31 16:02:19

+0

你确定*你需要使用'awk'吗?Ruby无法做到这一点,而且从'awk'翻译成Ruby通常很简单。如果你确实需要使用外部命令,['popen3'](http://ruby-doc.org/stdlib-2.1.2/libdoc/open3/rdoc/Open3.html)是实现它的方法,给你完全控制输入和输出。 – tadman 2014-10-31 16:03:41

+0

看起来很有趣,是否有一种方法可以组合多个命令(如上面的两个awk调用)? – nachocab 2014-10-31 16:12:35

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