2014-10-31 37 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