Open3.popen3 函数打开 bz、gz 和 txt 文件时出现错误“没有这样的文件或目录”或者“未打开阅读”?

发布于 2024-12-21 15:37:04 字数 1955 浏览 6 评论 0原文

我正在尝试编写一个实用程序函数来打开三种不同类型的文件:.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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我一直都在从未离去 2024-12-28 15:37:04

来自精细手册(写得相当混乱):

*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

Open3.popen3 不需要通常的模式标志 ("r+"),因为它会单独处理阅读、写作和错误;而且,正如您所看到的,尝试提供模式字符串只会导致错误和混乱。

第二种情况:

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

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

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

From the fine manual (which is rather confusingly written):

*popen3(cmd, &block)
[...]
So a commandline string and list of argument strings can be accepted as follows.

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

So when you do this:

Open3.popen3("cat /tmp/test.txt", "r+")

popen3 thinks that the command name is cat /tmp/test.txt and r+ is an argument to that command, hence the specific error that you're seeing:

No such file or directory - cat /tmp/test.txt

There's no need for the usual mode flags ("r+") with Open3.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:

Open3.popen3("cat", file, "r+") { |stdin, stdout, stderr|
  stdin.each {|line|
    #...

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 to stdout.read instead.

You should be building your commands as arrays and your match calls should be a little stricter:

if file.match(/\.bz2\z/) then
  cmd = [ 'bzcat', file ]
elsif file.match(/\.gz\z/) then
  cmd = [ 'gunzip', '-c', file ]
else
  cmd = [ 'cat', file ]
end

and then splat them:

Open3.popen3(*cmd) do |stdin, stdout, stderr|
  #...
end

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 the Open3.popen3 for the non-compressed cases and using File.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).

终遇你 2024-12-28 15:37:04

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

You'd better use bzip2-ruby and GzipReader for reading corresponding files. Opening a separate process for that is too expensive, complex and fragile.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文