Ruby中的面向行的流式传输(如grep)

Pet*_*ter 12 ruby pipe

默认情况下,Ruby打开$stdin$stdout处于缓冲模式.这意味着您不能使用Ruby来执行类似grep的操作过滤文本.有没有办法强迫Ruby使用面向行的模式?我见过的各种解决方案,包括popen3(其中只做缓冲模式)和pty(不分开处理$stdout$stderr,这是我需要).

我该怎么做呢?Python似乎也有同样的缺点.

use*_*769 4

看起来你最好的选择是使用 STDOUT.syswrite 和 STDOUT.sysread - 尽管代码丑陋,但以下内容似乎具有相当好的性能:

STDIN.sync = true
STDOUT.syswrite "Looking for #{ARGV[0]}\n"

def next_line
  mybuff = @overflow || ""
  until mybuff[/\n/]
    mybuff += STDIN.sysread(8)
  end
  overflow = mybuff.split("\n")
  out, *others = overflow
  @overflow = others.join("\n")
  out
rescue EOFError => e
  false  # NB: There's a bug here, see below
end

line = next_line
while line
  STDOUT.syswrite "#{line}\n" if line =~ /#{ARGV[0]}/i
  line = next_line
end
Run Code Online (Sandbox Code Playgroud)

注意:不确定您是否需要 #sync 与 #sysread,但如果是这样,您可能也应该同步 STDOUT。另外,它一次将 8 个字节读入 mybuff - 您应该尝试这个值,它的效率非常低/CPU 负担很重。最后,这段代码很糟糕,需要重构,但它有效 - 使用ls -l ~/* | ruby rgrep.rb doc(其中“doc”是搜索词)对其进行了测试


第二个注意事项:显然,我太忙于让它表现良好,但没能让它正确执行!正如 Dmitry Shevkoplyas 所指出的,如果在引发 EOFError 时 @overflow 中有文本,则该文本将丢失。我相信如果您将 catch 替换为以下内容,应该可以解决问题:

rescue EOFError => e
  return false unless @overflow && @overflow.length > 0
  output = @overflow
  @overflow = ""
  output
end
Run Code Online (Sandbox Code Playgroud)

(如果您觉得有帮助,请为德米特里的回答点赞!)