From: GP lisper
Subject: run-program and pipes
Date: 
Message-ID: <slrnfa8noe.p65.spambait@phoenix.clouddancer.com>
There are several command-line *nix programs that lend themselves to
easy manipulation with lisp.  'ltk' certainly heads my list of them,
another is gnuplot.  One feature that I'd like to obtain in these pipe
techniques is a copy of the datastream sent to the commandline
program.  For example:
------------------------------------------------------------
;;; PLOT CLASS
(defclass plot ()
 ( (series    :initform ()
              :allocation :instance)
   (seriesf   :initform (make-hash-table))
   
   (title     :initform nil)
   (xlabel    :initform nil)
   (x2label   :initform nil)
   (ylabel    :initform nil)
   (y2label   :initform nil)
   
   (process   :initform (extensions:run-program
			 "gnuplot"        ; normal program
			 nil              ; no args needed
                         :wait   nil      ; wait for child
                         :pty    nil      ; no terminal
                         :input  :stream  ; need an input stream
                         :error  :output  ; print to stdout for debugging (probably never seen)
                         :output t)       ; print to stdout for debugging
              :allocation :instance)
   (stream    :initform nil) )
  (:documentation
   "A class for plotting data with gnuplot."))
------------------------------------------------------------
This works fine, I can even get limited animation.


Now I'd like to get the following to work:
------------------------------------------------------------
;;; PLOT CLASS
(defclass plot ()
 ( (series    :initform ()
              :allocation :instance)
   (seriesf   :initform (make-hash-table))
   
   (title     :initform nil)
   (xlabel    :initform nil)
   (x2label   :initform nil)
   (ylabel    :initform nil)
   (y2label   :initform nil)
   
   (process   :initform (extensions:run-program
                         "tee"            ; program to run
              (mapcar #'string-downcase '(last-plot.gnuplot #\| gnuplot)) ; <== doesn't work ???
                         :wait   nil      ; wait for child
                         :pty    nil      ; no terminal
                         :input  :stream  ; need an input stream
                         :error  :output  ; print to stdout for debugging (probably never seen)
                         :output t)       ; print to stdout for debugging
              :allocation :instance)
   (stream    :initform nil) )
  (:documentation
   "A tee'ed class for plotting data with gnuplot."))
------------------------------------------------------------

gnuplot will work fine behind 'tee' in a terminal, I get plots and
copies of the datastream.  However the above doesn't fully work, and I
don't know where to instrument the problem in order to see what
actually happens.

This is in CMUCL, a recent snapshot.  I get a copy of the datastream
(which can be used for the original problem that prompted this
change), but I wouldn't mind getting the plot too.

Is this a limitation of lisp's 'run-program' or is some other syntax
needed to sneak a working *nix pipe thru the reader?

-- 
Lisp:  empowering Impossible Thoughts since 1958

-- 
Posted via a free Usenet account from http://www.teranews.com

From: Raymond Toy
Subject: Re: run-program and pipes
Date: 
Message-ID: <sxdir8bur1n.fsf@rtp.ericsson.se>
>>>>> "GP" == GP lisper <········@CloudDancer.com> writes:

[snip]
    GP> This is in CMUCL, a recent snapshot.  I get a copy of the datastream
    GP> (which can be used for the original problem that prompted this
    GP> change), but I wouldn't mind getting the plot too.

    GP> Is this a limitation of lisp's 'run-program' or is some other syntax
    GP> needed to sneak a working *nix pipe thru the reader?

run-program basically calls fork and execve.  Pipes using "|" are
handled by the shell.  

I think this means:

1. You can implement "|" yourself with several calls to run-program,
   hooking the inputs and outputs together appropriately.

2. Use run-program to run /bin/sh (or some other shell), and pass in
   the appropriate args to the shell.

There might be other solutions.

Ray
From: Madhu
Subject: Re: run-program and pipes
Date: 
Message-ID: <m3abtnta2p.fsf@robolove.meer.net>
* GP lisper in <·······················@phoenix.clouddancer.com> :
[...]
| (process   :initform (extensions:run-program
|			 "gnuplot"        ; normal program
| 			 nil              ; no args needed
|                          :wait   nil      ; wait for child
|                          :pty    nil      ; no terminal
|                          :input  :stream  ; need an input stream
|                          :error  :output  ; print to stdout for debugging (probably never seen)
|                          :output t)       ; print to stdout for debugging
|               :allocation :instance)

| This works fine, I can even get limited animation.
|    (process   :initform (extensions:run-program
|                          "tee"            ; program to run
|               (mapcar #'string-downcase '(last-plot.gnuplot #\| gnuplot)) ; <== doesn't work ???
|                          :wait   nil      ; wait for child
|                          :pty    nil      ; no terminal
|                          :input  :stream  ; need an input stream
|                          :error  :output  ; print to stdout for debugging (probably never seen)
|                          :output t)       ; print to stdout for debugging
|               :allocation :instance)
[...]

I do not understand how you are using tee. The info page for tee shows:

	The `tee' command copies standard input to standard output and
	also to any files given as arguments.  This is useful when you
	want not only to send some data down a pipe, but also to save a
	copy.

i.e. the arguments to `tee' are always files that get written to.

| gnuplot will work fine behind 'tee' in a terminal, I get plots and
| copies of the datastream.  However the above doesn't fully work, and I
| don't know where to instrument the problem in order to see what
| actually happens.

In the terminal your shell is probably piping the standard output (which
tee writes to) to gnuplot. If you want your shell to handle the piping
you should write your command to a string and call

	 (EXT:run-program '("sh" "-c" "COMMAND | CAT && etc"))

However when you are calling gnuplot from EXT:RUN-PROGRAM, you are
already passing it an :INPUT :STREAM argument. So the input stream is
available and you can pipe in data by calling WRITE on
(EXT::PROCESS-INPUT PROCESS)

I assume you already know this and this is how you are already using
gnuplot; and letting the unix process quit by writing the gnuplot "exit"
command on that stream..

You can of course create pipes using pipe(2) and attach them to
streams. I cannot imagine a valid use case for doing this with
RUN-PROGRAM though, unless you were doing some
`multiprocessing'. Sketch:

(defvars $stream $read-fd $write-fd)
(multiple-value-setq ($read-fd $write-fd) (unix:unix-pipe))
(setq $stream
      (make-two-way-stream
       (sys:make-fd-stream $read-fd :input t :buffering :none :auto-close t)
       (sys:make-fd-stream $write-fd :output t :buffering :none
			   :auto-close t)))

#||
(write-line "foobar" $stream)
(read-line $stream)
(close $stream)
(mapcar 'unix:unix-close (list $read-fd $write-fd))
||#

--
Madhu
From: Richard M Kreuter
Subject: Re: run-program and pipes
Date: 
Message-ID: <87zm1m0yxf.fsf@tan-ru.localdomain>
GP lisper <········@CloudDancer.com> writes:

> There are several command-line *nix programs that lend themselves to
> easy manipulation with lisp.  'ltk' certainly heads my list of them,
> another is gnuplot.  One feature that I'd like to obtain in these pipe
> techniques is a copy of the datastream sent to the commandline
> program.
<snip>
> gnuplot will work fine behind 'tee' in a terminal, I get plots and
> copies of the datastream.  However the above doesn't fully work, and I
> don't know where to instrument the problem in order to see what
> actually happens.

Common Lisp has a built-in solution for sending stream output to
multiple destinations: broadcast-streams.  (The reverse problem,
duplicating input from a stream to a destination, is what echo-streams
are for.)

Example:

(let ((process (run-program "tr" '("a-z" "A-Z")
			    :input :stream
			    :output :stream
			    :wait nil
			    :search t)))
   (unwind-protect
	(let ((pinput (process-input process))
	      (poutput (process-output process)))
	  (format
	   t "~&Input to tr: ~S~%Output from tr: ~S~%"
	   (with-output-to-string (string-stream)
	     (with-open-stream (to-process
				(make-broadcast-stream
				 pinput string-stream))
	       (write-line "foo" to-process))
	     ;; Programs that use fully buffered I/O will typically
	     ;; hang in a blocking read, so close write end of the
	     ;; pipe to ensure that the child receives this input.
	     (close pinput))
	   (with-output-to-string (another-string-stream)
	     (loop
		for char = (read-char poutput nil)
		always char
		do (write-char char another-string-stream)))))
     (process-close process)))
Input to tr: "foo
"
Output from tr: "FOO
"
=> NIL