Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 停止接受管道上的输入,但读取缓冲数据_Ruby_Linux - Fatal编程技术网

Ruby 停止接受管道上的输入,但读取缓冲数据

Ruby 停止接受管道上的输入,但读取缓冲数据,ruby,linux,Ruby,Linux,我有一个应用程序,它读取$stdin,并对数据进行一些处理。我想加入一个信号处理程序来捕获SIGINT/SIGTERM并优雅地关闭(意味着完成数据处理并在完成后退出)。棘手的部分是,我希望它停止从STDIN读取数据,但能够处理任何缓冲数据。这样,就可以启动另一个应用程序并将其传递到相同的STDIN管道,然后在前一个应用程序停止的地方继续处理 问题是,如果我关闭STDIN,缓冲的内容就会丢失,或者至少无法访问 基本上我正在尝试: #!/usr/bin/ruby Signal.trap('INT'

我有一个应用程序,它读取
$stdin
,并对数据进行一些处理。我想加入一个信号处理程序来捕获SIGINT/SIGTERM并优雅地关闭(意味着完成数据处理并在完成后退出)。棘手的部分是,我希望它停止从STDIN读取数据,但能够处理任何缓冲数据。这样,就可以启动另一个应用程序并将其传递到相同的STDIN管道,然后在前一个应用程序停止的地方继续处理

问题是,如果我关闭STDIN,缓冲的内容就会丢失,或者至少无法访问

基本上我正在尝试:

#!/usr/bin/ruby

Signal.trap('INT') do
    $stdin.close
end

f = File.open('/tmp/out', 'a')
while (data = $stdin.read(4096)) != "" do
    f.write(data)
end
它立即在
$stdin.read
调用上发出一个
IOError
异常,即使我知道它读取了一些数据(strace显示了它)

(我不需要关闭管道,我这样做只是为了打破
while
循环。如果有更优雅的方法来打破循环并获取缓冲数据,我很乐意接受。)


我知道这种方法在操作系统级别有效(管道缓冲区在传递到另一个应用程序时会保留),因为我可以执行以下测试,并且不会丢失任何数据:

# source.rb
i = 0
loop do
    puts "%08d" % (i += 1)
end


解决此问题的一种方法是在关闭原始文件之前复制文件描述符,然后错误将中断循环,您可以从未关闭的重复文件句柄读取其余数据

(如果此代码不好,很抱歉,我不知道ruby)


在与之斗争了几天之后,我不得不放弃
IO.read
,改用
IO.sysread
,自己做缓冲。这个解决方案其实并没有那么复杂,下面是实现

Signal.trap('INT') do
    $stdin.close
end

def myread(bufio, bytes) # `bufio` is a StringIO object, `bytes` is bytes to read
    begin
        while bufio.size < bytes do
            bufio.write($stdin.sysread(bytes - bufio.size))
        end
    rescue SignalException, Interrupt, Errno::EINTR => e
        retry
    rescue SystemCallError, IOError, EOFError => e
        # nothing, we're done
    end
end
Signal.trap('INT')do
$stdin.close
结束
def myread(bufio,bytes)#`bufio`是StringIO对象,`bytes`是要读取的字节
开始
而bufio.size<字节
写入($stdin.sysread(字节-bufio.size))
结束
救援信号异常,中断,错误号::EINTR=>e
重试
救援系统调用错误,IOError,EOFError=>e
#没什么,我们完了
结束
结束


我的确切代码与使用AWS ruby SDK时的代码略有不同,因此
myread
方法实际上只是传递给
AWS::S3::S3Object的一个块。write

在关闭管道末端和打开新进程之间,您希望发生什么?如果客户端一直将数据推入管道,而没有任何内容在侦听,该怎么办?管道不是这样工作的。如果您需要更具弹性的机制,您可能必须使用消息队列系统来代替。@JimGarrison它应该用来缓冲数据。这正是管道的工作原理(至少在linux中,我不能代表其他操作系统)。编辑:查看您的示例代码,您正在复制
$stdout
。但是,当我在
$stdin
上尝试时,仍然会出现异常,因为管道关闭时,上一个
$stdin.read()
调用仍处于挂起状态。另外,当我复制STDIN并使用dup'd对象时,我仍然可以从传入流中读取数据。我不想从流中读取更多的数据,只需获取缓冲区中的数据即可。@Patrick在这种情况下,(据我所知)没有任何原语可以帮助您完成所需的操作。您给出的将管道连接到多个程序的shell示例只是重复,它实际上没有做任何与此示例不同的事情。我更想听听你关于打破循环的评论,但我可能误解了它。@Patrick如果不尽快读取缓冲数据(非阻塞,因此你知道何时结束),你就无法获取缓冲数据。但问题是它可能永远不会结束,因为管道的重新填充速度可能比清空速度快。此外,修复了标准输入/标准输出错误,但不确定这是如何发生的。
ruby /tmp/source.rb | while true; do ruby reader.rb; sleep 1; done
00000001
00000002
00000003
00000004
00000005
#!/usr/bin/ruby

require 'fcntl'

stdin_dup = nil

Signal.trap('INT') do
  stdin_dup = File.for_fd($stdout.fcntl(Fcntl::F_DUPFD))
  $stdin.close
end

f = File.open('/tmp/out', 'a')
begin
  while (data = $stdin.read(4096)) != "" do
    f.write(data)
  end
rescue IOError
  # finish stuff with stdin_dup here
end
Signal.trap('INT') do
    $stdin.close
end

def myread(bufio, bytes) # `bufio` is a StringIO object, `bytes` is bytes to read
    begin
        while bufio.size < bytes do
            bufio.write($stdin.sysread(bytes - bufio.size))
        end
    rescue SignalException, Interrupt, Errno::EINTR => e
        retry
    rescue SystemCallError, IOError, EOFError => e
        # nothing, we're done
    end
end