Stop receiving input on the handset, but read data with buffering

I have an application that reads with $stdin and does some data processing. I want to turn on the signal handler to catch SIGINT / SIGTERM and finish work (which means completion of data processing and exit after completion). The tricky part is that I want it to stop reading from STDIN, but be able to process any buffered data. This means that you can start another application and transfer the same STDIN channel and resume processing when the previous application stopped.

The problem is that if I close STDIN, everything that was buffered is lost, or at least not available.

I am basically trying to do this:

 #!/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 

It immediately IOError exception in the call to $stdin.read , although I know that it is reading some data (strace shows it).

(I don’t need to close the handset, I just do it to break the while . If there is a more elegant way to break the loop and get buffered data, I would gladly accept it.)


I know that this methodology works at the operating system level (the buffer buffer is preserved when transferred to another application), since I can run the following test and not lose any data:

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

.

 # reader.rb $stdout.write($stdin.read(9)) $stdin.close 

.

 ruby /tmp/source.rb | while true; do ruby reader.rb; sleep 1; done 00000001 00000002 00000003 00000004 00000005 
+4
source share
2 answers

After IO.read this for several days, I had to give up IO.read and use IO.sysread instead and make my own buffering. The solution to this is really not that difficult, and below is the implementation.

 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 

My exact code is slightly different from the fact that I am using the ruby ​​SDK SDK, so the myread method is actually just the block passed to AWS::S3::S3Object.write

0
source

One way to solve this problem would be to duplicate the file descriptor before closing the original, then the error breaks the loop, and you can read the rest of the data from the closed file duplicate descriptor.

(Sorry if this code is bad, I don't know ruby)

 #!/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 
0
source

Source: https://habr.com/ru/post/1493771/


All Articles