[Haskell-cafe] Re: Writing an 'expect'-like program with
runInteractiveCommand
Graham Fawcett
graham.fawcett at gmail.com
Fri May 2 09:55:28 EDT 2008
On Fri, May 2, 2008 at 12:16 AM, Donn Cave <donn at avvanta.com> wrote:
> Graham Fawcett wrote:
>
>
> > I would like to communicate with an external, line-oriented process,
> > which takes a sequence of one-line commands, each returning an
> > arbitrary number of lines, and waits for another command after each
> > response. So, something like:
> > sendCmd :: (Handle, Handle) -> String -> IO [String]
> You may need to make some compromises to get this within the realm
> of the possible, if I understand your objective.
> - There is no way (at least on UNIX) to know when a read has been posted
> on the other end of your pipe/socket/pty/whatever...
Yes, and I should have realized this. After my post, I tried writing
the same program in another language, and ran into the same problems.
Thanks very much for the response. I'm going to play with your
example, and see if I can make it work in this case (and if not, I
will still learn from studying it!).
In case it's of interest, I see that E.W. Karlsen wrote a series of
articles (ten years ago!) on using Haskell to manage asynchronous
processes, and included an "expect-like" tool in his UniForM
Workbench:
http://www.informatik.uni-bremen.de/~ewk/WB.html
By the way, what I'm trying to do is to interact with a Berkeley XML
database; there's no existing Haskell wrapper for its API, and for
this particular task the cost of writing one is too high. I was going
to interact with the 'dbxml' command-line utility as a poor-man's FFI.
Given the issues you've raised, instead I might use a language that
has an existing DBXML interface, and call that from Haskell (e.g.
Python via MissingPy).
Thanks again,
Graham
> So you can't tell in this
> way when the external process has sent the last of the arbitrary number
> of
> lines and is now waiting for another command. If you have another way to
> know, or it doesn't really matter, then fine.
>
> - The vast majority of `line-oriented' software are actually going to block
> buffer
> their output when writing to a pipe, because that's what C I/O does. If
> you're
> lucky, the program you're dealing with here will flush its output before
> it reads,
> but if it doesn't, you're hosed - there isn't any way to talk to this
> program
> `interactively' on a pipe. In this case, you need a pseudotty device, a
> sort of
> pipe that supports tty device ioctls.
>
> - Buffering on your side of the I/O is of course also worse than useless.
>
> I see the GHC 6.8 library supports pseudottys, so for general amusement
> I submit below a small demonstration program. Unfortunately it doesn't
> entirely work. The pseudotty works, but I'm unable to turn off ECHO on the
> slave. So each master line yields two slave lines, and my program expects
> only one and gets behind on that account. So the command has to include
> its own "stty -echo". The commented lines attempt to turn of ECHO, but on
> MacOS X that causes the program to fail mysteriously.
>
> Donn Cave, donn at avvanta.com
> --------------------------------------------------
> import System.Posix.Terminal (TerminalMode(..), TerminalState(..),
> withoutMode, getTerminalAttributes, setTerminalAttributes,
> openPseudoTerminal, getSlaveTerminalName)
> import System (getArgs)
> import System.Posix.Types (Fd, ProcessID)
> import System.Posix.Process (forkProcess, executeFile)
> import System.Posix.IO (stdInput, stdOutput, closeFd, dupTo, fdWrite,
> fdRead)
>
> pchild :: Fd -> IO () -> IO ()
> pchild slaveFd exec = do
> dupTo slaveFd stdInput
> dupTo slaveFd stdOutput
> closeFd slaveFd
> exec
>
> ptyOpen :: IO () -> IO (ProcessID, Fd)
> ptyOpen exec = do
> (master, slave) <- openPseudoTerminal
> -- tc <- getTerminalAttributes slave
> -- let tc = withoutMode tc EchoLF
> -- setTerminalAttributes slave tc WhenDrained
> pid <- forkProcess (pchild slave exec)
> closeFd slave
> return (pid, master)
>
> ioact p0 p1 m0 m1 = do
> (d, n) <- fdRead m0 512
> fdWrite p1 d
> (e, n) <- fdRead p0 512
> fdWrite m1 e
> ioact p0 p1 m0 m1
>
> main = do
> (cmd:args) <- getArgs
> (pid, fd) <- ptyOpen (executeFile cmd True args Nothing)
> ioact fd fd stdInput stdOutput
>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>
More information about the Haskell-Cafe
mailing list