Frank Ruben
Frank Ruben

Reputation: 105

Cannot get input stream working in SBCL sb-ext:run-program

While the following works:

(let* ((i (make-string-input-stream "foo bar baz"))
       (p (sb-ext:run-program "/bin/cat" '() 
              :input i :output *trace-output* :wait t)))
  (sb-ext:process-close p))

the code below does not - it will stop after writing "001":

(let* ((_1 (format t "001~%"))
       (p (sb-ext:run-program "/bin/cat" '() 
              :input :stream :output *trace-output* :wait t))
       (_2 (format t "010~s~%" p))
       (s (sb-ext:process-input p)))
  (declare (ignore _1 _2))
  (format s "foo bar baz~%")
  (finish-output s)
  (sb-ext:process-close p))

So it seems to silently leave execution in sb-ext:run-program.

This is with SBCL 1.3.6 on Ubuntu 16.04.1.

Any ideas? Thanks in advance, Frank

Upvotes: 1

Views: 452

Answers (2)

Frank Ruben
Frank Ruben

Reputation: 105

This is what works, as suggested by jkiiski:

(let* ((p (sb-ext:run-program "/bin/cat" '() 
                              :input :stream 
                              :output *standard-output* 
                              :wait nil))
       (s (sb-ext:process-input p)))
  (format s "foo bar baz~%")
  (finish-output s)
  (sb-ext:process-wait p)
  (sb-ext:process-close p))

Upvotes: 0

jkiiski
jkiiski

Reputation: 8411

As I mentioned in the comments, the problem is the :WAIT T argument. It causes the call to SB-EXT:RUN-PROGRAM to not return until the child process exits.

In the first example you passed a string input stream to the child process. cat will read input from the stream, and when the input ends there will be a End of File, so cat exits. In the second example there is no input available for the program, so it's effectively an infinite loop (just like if you run cat on the command line, and don't give any input to it; it will never exit).

The solution is to use :WAIT NIL. You will also have to close the input stream with CLOSE, because otherwise there will be no EOF and cat keeps listening for more input. You'll also want to use SB-EXT:PROCESS-WAIT after closing the stream to wait for cat to exit itself.

(let* ((p (sb-ext:run-program "/bin/cat" '() 
                              :input :stream
                              :output *standard-output*
                              :wait nil))
       (s (sb-ext:process-input p)))
  (format s "foo bar baz~%")
  (finish-output s)
  (close s)
  (sb-ext:process-wait p)
  (sb-ext:process-close p))

I'm not sure why you used *TRACE-OUTPUT* for the child output, so I changed it to *STANDARD-OUTPUT*.

Also, using FORMAT for debugging like that is kind of ugly. Common Lisp provides actual debugging tools. In this case you could use STEP:

(step (let* ((p (sb-ext:run-program "/bin/cat" '() 
                                    :input :stream
                                    :output *standard-output*
                                    :wait nil))
             (s (sb-ext:process-input p)))
        (format s "foo bar baz~%")
        (finish-output s)
        (close s)
        (sb-ext:process-wait p)
        (sb-ext:process-close p)))

This will put you in the debugger, showing the call being evaluated next. You can invoke the STEP-NEXT-restart to continue to the next call.

Upvotes: 3

Related Questions