2

我正在尝试编写一个实用函数来打开三种不同类型的文件:.bz2、.gz 和 .txt。我不能只使用File.read它,因为它给了我压缩文件的垃圾。我正在尝试使用Open3.popen3,以便我可以给它一个不同的命令,但我得到一个“没有这样的文件或目录”错误,代码如下:

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
  }
}

我没有收到任何错误,并且打印了 STDOUT 行,但没有一行语句打印出任何内容。

在尝试了几种不同的方法后,我想出的解决方案是:

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
4

2 回答 2

5

精美的手册(写得相当混乱):

* popen3( cmd, &block)
[...]
所以命令行字符串和参数字符串列表可以接受如下。

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.txt并且r+是该命令的参数,因此您看到的特定错误:

没有这样的文件或目录 - 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

这不仅有效,而且可以让您避免使用有趣的文件名。

您还可以通过跳过未压缩的情况并改为使用来避免无用的使用cat(有人可能会抱怨)。您可能还需要考虑检查文件的字节以查看它包含的内容,而不是依赖扩展名(或使用ruby​​-filemagic来检查)。Open3.popen3File.open

于 2011-12-15T01:50:30.777 回答
1

您最好使用bzip2-rubyGzipReader来读取相应的文件。为此打开一个单独的流程过于昂贵、复杂和脆弱。

于 2011-12-15T18:19:01.923 回答