How to detect if input stream is empty (but not EOF)?
Asked Answered
D

1

6

I am spawning a process from Common Lisp program (gnuplot). I am able to establish input and output streams for the process. However, I have a problem reading from the output. The problem is that I want to try to read from the output, and if there is nothing there, well... do nothing for example.

(Basic problem: I want to read the result of the command show term but I want to skip any other output that gnuplot might have produced before sending this command)

If I just use (read-line gnuplot-output nil :eof) and there is nothing in output stream, it will not indicate :eof (since the stream is still alive and something might appear there) and will just block until it has something to read (i.e. forever).

Is there are way to detect that there is nothing to read? At least, somehow safely time-out the attempt to read (i.e. it shouldn't pop a new line out of the stream once the time-out is reached)?

PS. I am using SBCL

Dwight answered 25/1, 2016 at 10:41 Comment(0)
A
5

Listen should tell you if there is a character available. I think you'll have to read the stream character by character rather than whole lines at a time, unless you're sure that the program never outputs an incomplete line.

Edit: A quick test (using sb-ext for running the program):

(defun test ()
  (do* ((program (run-program "output-test.sh" nil
                              :search t
                              :output :stream
                              :wait nil))
        (output-stream (process-output program)))
       ((not (process-alive-p program)))
    (if (listen output-stream)
        (loop
           for char = (read-char-no-hang output-stream nil nil)
           while char
           do (write-char char))
        (format t "No output available.~%"))
    (sleep 1)))

Where output-test.sh is:

#!/bin/sh
for item in *
do
    echo $item
    sleep 3
done
Anemometer answered 25/1, 2016 at 10:54 Comment(2)
With read-char-no-hang, you don't need to use listen. Also, collapsing variable bindings from an outer let/let* to the inner do/do* as you did in one of your edits hinders code readability and maintainability, only to save a few characters. Both program and output-stream don't vary in the loop, so leaving them in an outer let/let* would make that obvious.Physostomous
@acalent The listen is to check if there is anything to read, without reading it. Using only read-char-no-hang would consume the first character of the input, making the logic more complicated. As for having the variable bindings in a separate let, I did think that would be clearer at first, but then figured that since the loop is supposed to run until the program exits, it makes sense to have the program started in the loops initialization. That way the entire lifetime of the child process is in one place.Anemometer

© 2022 - 2024 — McMap. All rights reserved.