Функция Open3.popen3 для открытия ошибок файлов bz, gz и txt с «Нет такого файла или каталога» или «не открыт для чтения»? - PullRequest
1 голос
/ 15 декабря 2011

Я пытаюсь написать служебную функцию, которая будет открывать файлы трех разных типов: .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.

Я решил жестко закодировать его, чтобы сделать текстовый файл следующим образом:

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

Ответы [ 2 ]

4 голосов
/ 15 декабря 2011

Из прекрасного руководства (которое написано довольно запутанно):

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 (на что кто-то, вероятно, будет жаловаться), пропустив Open3.popen3 для несжатых случаев и используя вместо этого File.open. Возможно, вы захотите проверить байты файла, чтобы увидеть, что в нем содержится, а не полагаться на расширение (или использовать для проверки ruby-filemagic ).

1 голос
/ 15 декабря 2011

Вам лучше использовать bzip2-ruby и GzipReader для чтения соответствующих файлов. Открытие отдельного процесса для этого слишком дорого, сложно и хрупко.

...