From: Mirko
Subject: capturing clisps ext:run-program output
Date: 
Message-ID: <45ed17a3-47f7-4265-9189-80539d0f576d@r15g2000prd.googlegroups.com>
Hello,

I am running clisp on windows+cygwin, and I am trying to capture the
output of the `cygpath' command in a variable.  If I do:
 (ext:run-program "cygpath" :arguments (list "-m" "/c/foo"))
I will get c:/foo at the REPL (or *inferior-lisp* in emacs+slime)

To capture the output in a variable I modified a prior post by Pascal
Bourgignon, and I get close.  The following code
(flet ((eat (stream)
	 (loop
	    while (listen stream)
	    collect (read-char-no-hang stream nil nil))))
  (with-open-stream (io (ext:run-program "cygpath" :arguments (list "-
m" "/c/temp")
					 :output :stream))
    (print (read-char io))
    (coerce  (eat io) 'string)))

will capture all but the first character, which is read by the (`read-
char io)' statement.
My problem is that if I remove that read-char, I get nil -- nothing
gets read.  What is happening here?

Thanks,

Mirko

From: Pascal J. Bourguignon
Subject: Re: capturing clisps ext:run-program output
Date: 
Message-ID: <7ciqpb8etb.fsf@pbourguignon.anevia.com>
Mirko <·············@gmail.com> writes:

> Hello,
>
> I am running clisp on windows+cygwin, and I am trying to capture the
> output of the `cygpath' command in a variable.  If I do:
>  (ext:run-program "cygpath" :arguments (list "-m" "/c/foo"))
> I will get c:/foo at the REPL (or *inferior-lisp* in emacs+slime)
>
> To capture the output in a variable I modified a prior post by Pascal
> Bourgignon, and I get close.  The following code
> (flet ((eat (stream)
> 	 (loop
> 	    while (listen stream)
> 	    collect (read-char-no-hang stream nil nil))))
>   (with-open-stream (io (ext:run-program "cygpath" :arguments (list "-m" "/c/temp")
> 					 :output :stream))
>     (print (read-char io))
>     (coerce  (eat io) 'string)))
>
> will capture all but the first character, which is read by the (`read-
> char io)' statement.
> My problem is that if I remove that read-char, I get nil -- nothing
> gets read.  What is happening here?

Think about it, step by step.


1- ext:run-program creates pipes and forks a process.
2- with-open-stream binds io to the output stream.
3- (eat io) is called
3.1- (listen io) is called
3.1.1-  there is no input so listen returns nil.
3.2- loop while terminates
3.3- eat returns nil
4- coerce coerces nil to a string.
5- flet returns "NIL".
6- the system at least gives some CPU time to the forked process.
7- it exec the program
8- the program sends something to the pipe
9- the program terminates.
10- perhaps sometime in the future, the garbage is collected.

-- 
__Pascal Bourguignon__
From: Mirko
Subject: Re: capturing clisps ext:run-program output
Date: 
Message-ID: <1032c70d-6723-430f-8898-e2ae6a8a9247@x14g2000yqk.googlegroups.com>
On Dec 23, 3:40 am, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
> Mirko <·············@gmail.com> writes:
> > Hello,
>
> > I am running clisp on windows+cygwin, and I am trying to capture the
> > output of the `cygpath' command in a variable.  If I do:
> >  (ext:run-program "cygpath" :arguments (list "-m" "/c/foo"))
> > I will get c:/foo at the REPL (or *inferior-lisp* in emacs+slime)
>
> > To capture the output in a variable I modified a prior post by Pascal
> > Bourgignon, and I get close.  The following code
> > (flet ((eat (stream)
> >     (loop
> >        while (listen stream)
> >        collect (read-char-no-hang stream nil nil))))
> >   (with-open-stream (io (ext:run-program "cygpath" :arguments (list "-m" "/c/temp")
> >                                     :output :stream))
> >     (print (read-char io))
> >     (coerce  (eat io) 'string)))
>
> > will capture all but the first character, which is read by the (`read-
> > char io)' statement.
> > My problem is that if I remove that read-char, I get nil -- nothing
> > gets read.  What is happening here?
>
> Think about it, step by step.
>
> 1- ext:run-program creates pipes and forks a process.
> 2- with-open-stream binds io to the output stream.
> 3- (eat io) is called
> 3.1- (listen io) is called
> 3.1.1-  there is no input so listen returns nil.
> 3.2- loop while terminates
> 3.3- eat returns nil
> 4- coerce coerces nil to a string.
> 5- flet returns "NIL".
> 6- the system at least gives some CPU time to the forked process.
> 7- it exec the program
> 8- the program sends something to the pipe
> 9- the program terminates.
> 10- perhaps sometime in the future, the garbage is collected.
>
> --
> __Pascal Bourguignon__

I thought that step 1, `run-program', will run the program, and place
the output `into' the stream.  You are saying that it is the read-char
that gives the program room to breathe and fill the stream.  I will
have to think about this more.

I guess I can then read the stream via loop by (I am away from my repl
right now)
(loop
:collect ...
:unless (listen stream))

Thanks
From: Pascal J. Bourguignon
Subject: Re: capturing clisps ext:run-program output
Date: 
Message-ID: <7caban7y9t.fsf@pbourguignon.anevia.com>
Mirko <·············@gmail.com> writes:

> On Dec 23, 3:40�am, ····@informatimago.com (Pascal J. Bourguignon)
> wrote:
>> Think about it, step by step.
>>
>> 1- ext:run-program creates pipes and forks a process.
>> 2- with-open-stream binds io to the output stream.
>> 3- (eat io) is called
>> 3.1- (listen io) is called
>> 3.1.1- �there is no input so listen returns nil.
>> 3.2- loop while terminates
>> 3.3- eat returns nil
>> 4- coerce coerces nil to a string.
>> 5- flet returns "NIL".
>> 6- the system at least gives some CPU time to the forked process.
>> 7- it exec the program
>> 8- the program sends something to the pipe
>> 9- the program terminates.
>> 10- perhaps sometime in the future, the garbage is collected.
>>
> I thought that step 1, `run-program', will run the program, and place
> the output `into' the stream.  You are saying that it is the read-char
> that gives the program room to breathe and fill the stream.  I will
> have to think about this more.

When you ask the output to be collected thru a stream, the lisp
process doesn't wait for the forked program to finish, it lets you
read (and possibly write) thru the pipes.


> I guess I can then read the stream via loop by (I am away from my repl
> right now)
> (loop
> :collect ...
> :unless (listen stream))

Still, LISTEN is problematic.  The reason why there is no byte in the
pipe may be because the other program is slow to fill it.

Either take into account time, or _parse_ what you expect from the program.

In the case of cygpath, it should output a single line always.
So you could count on EOF and just write:

C/USER[29]> (defun shell-command-to-string (command &rest arguments)
              (with-open-stream (inp (ext:run-shell-command (apply (function format) nil command arguments) :output :stream))
                (with-output-to-string (out)
                  (loop
                     :for line = (read-line inp nil nil)
                     :while line :do (write-line line out)))))
SHELL-COMMAND-TO-STRING
C/USER[30]> (string-trim #(#\newline) (shell-command-to-string "hostname"))
"simias"
C/USER[31]> (shell-command-to-string "echo hello ; echo world")
"hello
world
"
C/USER[32]> 

and you could define:

(defun cygpath (path)
   (string-trim #(#\newline)  (shell-command-to-string "cygpath -m ~S" path)))


-- 
__Pascal Bourguignon__
From: budden
Subject: Re: capturing clisps ext:run-program output
Date: 
Message-ID: <35e5ace6-5cdd-4fe9-b272-295120c8f82d@d36g2000prf.googlegroups.com>
Hi!
  If your program is non-interactive, you can redirect its output to a
file.
Write a shell scirpt /c/mydir/cygpath.sh with contents:

cygpath -m /c/foo > /c/mydir/run_program_output 2> /c/mydir/
run_program_stderror_ouptut

Then run
(ext:run-program "bash" :arguments '("/c/mydir/cygpath.sh"))

Then read files run_probram_output and run_program_stderror_output
I didn't work with clisp and cygwin so there might be some problems,
but it is most likely that they can be solved
easily.