2

I was hoping to experiment with cl-async to run a series of external programs with a large combinations of command line arguments. However, I can't figure out how to read the stdout of the processes launched with as:spawn.

I would typically use uiop which makes it easy to capture the process output:

(let ((p (uiop:launch-program ... :output :stream)))
  (do-something-else-until-p-is-done)
  (format t "~a~%" (read-line (uiop:process-info-output p))))

I've tried both :output :pipe and :output :stream options to as:spawn and executing (as:process-output process-object) in my exit-callback shows the appropriate pipe or async-stream objects but I can't figure out how to read from them.

Can anyone with experience with this library tell how to accomplish this?

2 Answers 2

1

So you go to your repl and type:

CL-USER> (documentation 'as:spawn 'function)

And you read whatever comes out (or put your point on the symbol and hit C-c C-d f). If you read it you’ll see that the format for the :input, etc arguments is either :pipe, (:pipe args...), :stream, or (:stream args...) (or some other options). And that :stream behaves similarly to :pipe but gives output of a different type and that for details of args one should look at PIPE-CONNECT so you go and look up the documentation for that. Well it tells you what the options are but it isn’t very useful. What’s the documentation/description of PIPE or STREAM? Well it turns out that pipe is a class and a subclass of STREAMISH. What about PROCESS that’s a class too and it has slots (and accessors) for things like PROCESS-OUTPUT. So what is a good plan for how to figure out what to do next? Here’s a suggestion:

  1. Spawn a long running process (like cat foo.txt -) with :output :stream :input :pipe say
  2. Inspect the result (C-c C-v TAB)
  3. Hopefully it’s an instance of PROCESS. What is it’s output? Inspect that
  4. Hopefully the output is a Gray stream (ASYNC-STREAM). Get it into your repl and see what happens if you try to read from it?
  5. And what about the input? See what type that has and what you can do with it

The above is all speculation. I’ve not tried running any of this but you should. Alternatively go look at the source code for the library. It’s already on your computer and if you can’t find it it’s on GitHub. There are only about half a dozen source files and they’re all small. Just read them and see what you can learn. Or go to the symbol you want to know about and hit M-. to jump straight to its definition. Then read the code. Then see if you can figure out what to do.

Sign up to request clarification or add additional context in comments.

2 Comments

DESCRIBE is better for interactive use than DOCUMENTATION. The latter is for programmatically accessing the documentation string.
This is true. I guess I just wanted to make the point that there was documentation. I think the best thing in most cases is a combination of the lambda list hints from slime and looking around with M-. and M-, and maybe a bit of grep
1

I found the answer in the test suite. The output stream can only be processed asynchronously via a read call-back. The following is simple example for posterity

(as:start-event-loop
  (lambda ()
    (let ((bytes (make-array 0 :element-type '(unsigned-byte 8))))
        (as:spawn "./test.sh" '()
                  :exit-cb (lambda (proc exit-status term-signal)
                              (declare (ignore proc exit-status term-signal))
                              (format t "proc output:~%~a"
                                (babel:octets-to-string bytes)))
                  :output (list :stream
                                :read-cb (lambda (pipe stream)
                                           (declare (ignore pipe))
                                           (let ((buf (make-array 128 :element-type '(unsigned-byte 8))))
                                             (loop for n = (read-sequence buf stream)
                                                while (plusp n) do
                                                   (setf bytes 
                                                        (concatenate  '(vector (unsigned-byte 8))
                                                          bytes
                                                         (subseq buf 0 n)))))))))))

with

$ cat test.sh
#!/bin/bash

sleep_time=$((1+$RANDOM%10))
echo "Process $$ will sleep for $sleep_time"
sleep $sleep_time
echo "Process $$ exiting"

yields the expected output

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.