2011-12-15 15 views
1

我想寫一個實用程序功能,將打開三種不同類型的文件:.bz2,.gz和.txt。我不能僅僅使用File.read,因爲它給了我壓縮文件的垃圾回收。我試圖用Open3.popen3,這樣我可以給它一個不同的命令,但我得到一個「沒有這樣的文件或目錄」錯誤與下面的代碼:Open3.popen3函數用'沒有這樣的文件或目錄'或'未打開讀取'來打開bz,gz和txt文件錯誤?

def file_info(file) 
    cmd = '' 
    if file.match("bz2") then 
    cmd = "bzcat #{file}"# | head -20" 
    elsif file.match("gz") then 
    cmd = "gunzip -C#{file}" 
    else 
    cmd = "cat #{file}" 
    end 

    puts "opening file #{file}" 
    Open3.popen3("#{cmd}", "r+") { |stdin, stdout, stderr| 
    puts "stdin #{stdin.inspect}" 
    stdin.read {|line| 
     puts "line is #{line}" 
     if line.match('^#') then 
     else 
     break 
     end 
    } 
    } 
end 


> No such file or directory - cat /tmp/test.txt 

該文件不存在。我試過使用cmd而不是#{cmd},結果與popen3 cmd相同。

我決定硬編碼它做的txt文件,如下所示:

def file_info(file) 
    puts "opening file #{file}" 
    Open3.popen3("cat", file, "r+") { |stdin, stdout, stderr| 
    puts "stdin #{stdin.inspect}" 
    stdin.read {|line| 
     puts "line is #{line}" 
     if line.match('^#') then 
     else 
     break 
     end 
    } 
    } 
end 

這讓我回:

stdin #<IO:fd 6> 
not opened for reading 

我在做什麼錯?

當我這樣做:

Open3.popen3("cat",file) { |stdin, stdout, stderr| 
    puts "stdout is #{stdout.inspect}" 
    stdout.read {|line| 
    puts "line is #{line}" 
    if line.match('^#') then 
     puts "found line #{line}" 
    else 
     break 
    end 
    } 
} 

我沒有得到任何錯誤和標準輸出線印刷,但也行語句打印出任何東西。

嘗試一些不同的東西后,我想出了一個解決辦法是:

cmd = Array.new 
if file.match(/\.bz2\z/) then 
    cmd = [ 'bzcat', file ] 
elsif file.match(/\.gz\z/) then 
    cmd = [ 'gunzip', '-c', file ] 
else 
    cmd = [ 'cat', file ] 
end 

Open3.popen3(*cmd) do |stdin, stdout, stderr| 
    puts "stdout is #{stdout}" 
    stdout.each do |line| 
    if line.match('^#') then 
     puts "line is #{line}" 
    else 
     break 
    end 
    end 
end 
+1

什麼`R +`標誌的目的是什麼? – buruzaemon 2011-12-15 01:05:23

回答

4

fine manual(這是相當容易混淆寫):

popen3(* CMD,&塊)
[...]
因此,命令行字符串和參數字符串列表可以被接受,如下所示。

Open3.popen3("echo a") {|i, o, e, t| ... } 
Open3.popen3("echo", "a") {|i, o, e, t| ... } 
Open3.popen3(["echo", "argv0"], "a") {|i, o, e, t| ... } 

所以,當你這樣做:

Open3.popen3("cat /tmp/test.txt", "r+") 

popen3認爲命令名稱爲cat /tmp/test.txtr+是一個參數,該命令,因此具體的錯誤,你所看到的:

沒有這樣的文件或目錄 - cat /tmp/test.txt

不需要通常的模式標誌("r+")和Open3.popen3,因爲它會分開處理讀取,寫入和錯誤;正如你所看到的,試圖提供模式字符串只會導致錯誤和混淆。

第二種情況:

Open3.popen3("cat", file, "r+") { |stdin, stdout, stderr| 
    stdin.each {|line| 
    #... 

不起作用,因爲stdin是命令的標準輸入,這就是你會讀什麼,你會想stdout.read代替。

你應該建立你的命令數組和你的match調用應該是有點嚴厲:

if file.match(/\.bz2\z/) then 
    cmd = [ 'bzcat', file ] 
elsif file.match(/\.gz\z/) then 
    cmd = [ 'gunzip', '-c', file ] 
else 
    cmd = [ 'cat', file ] 
end 

,然後啪他們:

Open3.popen3(*cmd) do |stdin, stdout, stderr| 
    #... 
end 

這不僅工作,但它會節省你從有趣的文件名。

您也可以通過跳過非壓縮情況下的Open3.popen3並使用File.open代替,來避免無用的使用cat(某人可能會抱怨)。您可能還需要考慮檢查文件的字節來看看它包含而不是依靠擴展名(或使用ruby-filemagic檢查你)。

+0

感謝您的信息。我用測試結果更新了我的問題(因爲我顯然無法在評論中做代碼)。 – 2011-12-15 18:00:24

+0

顯然我不這樣做.read在stdout,我。每個... – 2011-12-15 18:07:40

1

你最好使用bzip2-rubyGzipReader讀取相應的文件。爲此開放一個單獨的過程太昂貴,複雜和脆弱。

相關問題