Из прекрасного руководства (которое написано довольно запутанно):
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 ).