Open3.popen3 函数打开 bz、gz 和 txt 文件时出现错误“没有这样的文件或目录”或者“未打开阅读”?
我正在尝试编写一个实用程序函数来打开三种不同类型的文件:.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
I'm trying to write a utility function that will open three different types of files: .bz2, .gz, and .txt. I can't just use File.read
because it gives me garbage back for the compressed files. I'm trying to use Open3.popen3
so that I can give it a different command, but I'm getting a 'no such file or directory' error with the following code:
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
The file does exist. I've tried using cmd
instead of #{cmd}
with the same results in the popen3 cmd
.
I decided to hardcode it to do the txt file as follows:
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
This gives me back:
stdin #<IO:fd 6>
not opened for reading
What am I doing wrong?
When I do:
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
}
}
I get no errors and the STDOUT line is printed, but neither line statement prints out anything.
After trying several different things, the solution I came up with was:
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
来自精细手册(写得相当混乱):
因此,当您执行此操作时:
popen3
认为命令名称是cat /tmp/test.txt
并且r+
是该命令的参数,因此您看到的具体错误是:Open3.popen3
不需要通常的模式标志 ("r+"
),因为它会单独处理阅读、写作和错误;而且,正如您所看到的,尝试提供模式字符串只会导致错误和混乱。第二种情况:
不起作用,因为
stdin
是命令的标准输入,这就是您写入而不是从中读取的内容,您可以想要使用stdout.read
来代替。您应该将命令构建为数组,并且您的
match
调用应该更严格一些:然后将它们打出来:
这不仅有效,而且可以让您避免使用有趣的文件名。
您还可以通过在非压缩情况下跳过
Open3.popen3
并使用File 来避免无用地使用
来代替。您可能还需要考虑检查文件的字节以查看它包含的内容,而不是依赖扩展名(或使用 ruby-filemagic 为您检查)。cat
(有人可能会抱怨)。打开From the fine manual (which is rather confusingly written):
So when you do this:
popen3
thinks that the command name iscat /tmp/test.txt
andr+
is an argument to that command, hence the specific error that you're seeing:There's no need for the usual mode flags (
"r+"
) withOpen3.popen3
since it will separate handles for reading, writing, and errors; and, as you've seen, trying to supply the mode string just causes bugs and confusion.The second case:
Doesn't work because
stdin
is the command's standard input and that's what you would write to not read from, you'd want tostdout.read
instead.You should be building your commands as arrays and your
match
calls should be a little stricter:and then splat them:
Not only does this work but it will save you from funny filenames.
You could also avoid a useless use of
cat
(which someone will probably complain about) by skipping theOpen3.popen3
for the non-compressed cases and usingFile.open
instead. You might also want to consider checking the file's bytes to see what it contains rather than relying on the extension (or use ruby-filemagic to check for you).你最好使用 bzip2-ruby 和 GzipReader 用于读取相应的文件。为此开设一个单独的流程过于昂贵、复杂且脆弱。
You'd better use bzip2-ruby and GzipReader for reading corresponding files. Opening a separate process for that is too expensive, complex and fragile.