Lisp HUG Maillist Archive

How to send EOF to SYS:OPEN-PIPE?

How can I use SYS:OPEN-PIPE with programs that read until EOF,
i.e. how can I signal that I'm finished sending data to a program? The
example from the docs doesn't work in this case:

(with-open-stream
  (s (sys:open-pipe "perl -lne '$count++; END {print $count}'"
                    :direction :io))
  (write-line "foo" s)
  (write-line "bar" s)
  (force-output s)
  (read-line s))

I want this to return "2" but it hangs instead.

Thanks,
Edi.


Re: How to send EOF to SYS:OPEN-PIPE?

On Tuesday 28 October 2003 06:26 am, Edi Weitz wrote:
> How can I use SYS:OPEN-PIPE with programs that read until EOF,
....
> (with-open-stream
>   (s (sys:open-pipe "perl -lne '$count++; END {print $count}'"
....
> I want this to return "2" but it hangs instead.

If you run the line of perl by itself in bash, you find that it does not 
finish until you hit ^D (unix eof character).

The lisp code and the perl code are hanging, waiting for each other to send 
EOF.

This is a protocol problem.

You either need to open two separate streams - one output, one input - and 
close the output pipe before reading from the input, or, you need to rewrite 
the perl scriptlet so that it runs a simple protocol and "knows" when to quit 
(e.g. it quits when lisp sends it some specific character).

pt

p.s. After a quick glance at the manuals, I don't see how to open two streams 
on one shell command, so I would suggest altering the perl.


Re: How to send EOF to SYS:OPEN-PIPE?

On Tue, 28 Oct 2003 11:08:40 -0500, tarvydas <tarvydas@allstream.net> wrote:

> You either need to open two separate streams - one output, one input
> - and close the output pipe before reading from the input, or, you
> need to rewrite the perl scriptlet so that it runs a simple protocol
> and "knows" when to quit (e.g. it quits when lisp sends it some
> specific character).
> 
> p.s. After a quick glance at the manuals, I don't see how to open
> two streams on one shell command, so I would suggest altering the
> perl.

Altering the program is not an option, unfortunately. (It's a C
program and I don't have the source. I used the Perl snippet for
illustration purposes only.)

I had hopes that LW would allow for separate streams like CMUCL or
AllegroCL but it looks like this isn't possible. In CMUCL you can, for
example, do it like this:

  (let (prog)
    (unwind-protect
      (progn
        (setq prog
                (ext:run-program "/tmp/foo.pl" nil
                                 :output :stream
                                 :input :stream
                                 :wait nil))
        (with-open-stream (in (ext:process-input prog))
          (write-line "foo" in)
          (write-line "bar" in)
          (write-line "baz" in))
        (with-open-stream (out (ext:process-output prog))
          (with-output-to-string (s)
            (loop for line = (read-line out nil nil)
                  while line
                  do (write-string line s)))))
      (ext:process-close prog)))

("foo.pl" is a small Perl script which counts the lines coming from
stdin. This form will actually return the string "3".)

This doesn't work in CMUCL, BTW, if I replace "foo.pl" with the Perl
one-liner from my previous post but this doesn't bother me. I guess
you have to use the :PTY variant of RUN-PROGRAM in this case.

Thanks,
Edi.


Re: How to send EOF to SYS:OPEN-PIPE?

On Tue, 28 Oct 2003 17:12:13 -0500, tarvydas <tarvydas@allstream.net> wrote:

> Pending a reply from David Fox, have you tried the following kludges?:
> 
> a) if you only need the final reply from the C program, then use an
> input stream and redirect the output of the C program to a file
> (sys:with-open-pipe "cprog >/tmp/reply" :direction :output)
> 
> b) Write a small C / Perl / whatever program that invokes the big C
> program, redirecting stdin and stdout to two separate file
> descriptors.  The tiny program can run a small protocol with the
> lisp program.
> 
> c) You could probably do it all with FFI and direct calls to Unix
> exec, read and write.
> 
> d) Use two comm:socket-stream's in lisp.  Write two perl (C / Tcl/Tk
> / whatever) helpers - one which reads from a socket and writes to
> stdout, the other reads from stdin and writes to a socket, then do
> something like
> 
> 	(sys:run-shell-command "sock2stdout | cprog | stdin2sock")

Thanks for the helpful ideas! I'll try some of these unless someone
comes up with a LW feature I don't know of yet.

Thanks again,
Edi.


Updated at: 2020-12-10 08:59 UTC