2

.bz2、.gz、.txtの3種類のファイルを開くユーティリティ関数を作成しようとしています。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そして、ご覧のとおり、モード文字列を指定しようとすると、バグや混乱が生じるだけです。

2 番目のケース:

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 に答える