[squeak-dev] OSProcess and multiline input

David T. Lewis lewis at mail.msen.com
Fri Feb 24 23:51:19 UTC 2012

On Fri, Feb 24, 2012 at 02:00:12PM -0800, Eliot Miranda wrote:
> On Fri, Feb 24, 2012 at 1:53 PM, Sean P. DeNigris <sean at clipperadams.com>wrote:
> > I'm trying to pass a multiline argument, but none of the things that work
> > in
> > the shell seem to work with OSP.
> >
> > I passed both of the following to PipeableOSProcess>>waitForCommand: with a
> > "No such file or directory" error, which usually means that the multiline
> > part was not processed correctly. However, if I copy/paste them into the
> > shell, they work fine
> >
> > heredoc:
> > '/usr/bin/osascript -ss  <<-EOF
> > tell application "Safari" to activate
> > tell application "Terminal" to activate
> > EOF'
> >
> > double-quoted string:
> > '/usr/bin/osascript -ss  -e "
> > tell application \"Safari\" to activate
> > tell application \"Terminal\" to activate"'
> >
> > Why are these not working?
> >
> OSP is not a shell.  Ask yourself what a program receives when iyt is
> invoked with the two examples above.  Then provide the equivalent argument
> to OSP and you'll find it'll work.  OSP is an interface to exec.  A shell
> is an interface to exec.  OSP doesn't parse arguments, just passes them
> onto exec.  A shell does parse arguments before passing them onto exec.  So
> the shell input
> "
> tell application \"Safari\" to activate
> tell application \"Terminal\" to activate"
> is a string argument which in Smalltalk would be written
> (String with: Character lf), 'tell application "Safari" to
> activate', (String with: Character lf), 'tell application "Terminal" to
> activate'

Exactly so. PipeableOSProcess class>>command: will take the string you
provide and pass it directly to a unix shell (/bin/sh which in most cases
is a link to bash). It will exec that /bin/sh program, providing your
input string directly to /bin/sh for processing. In turn, /bin/sh does
its normal processing, and executes whatever programs you asked it to
run. This means that the string you provide as input should contain exactly
the characters that you would have typed on a keyboard and sent through
the terminal driver to the /bin/sh program. Any <cr> or <lf> characters,
quote marks, and so forth in the the string are passed directly to the
/bin/sh program, which is the program that actually executes your command.

This may seem confusing if you are trying to send multiple lines of text
to the shell, because the "multiple lines" are really just a string with
some <cr> or <lf> characters embedded. You think of them as lines of text
because the unix terminal driver handling your keyboard processes them
that way; it waits until you hit your "enter" key before passing a line
of input to the shell. But from the point of view of the shell, it is just
a stream of bytes being sent to the shell, and by convention the shell
treats a bunch of characters ending with a <lf> as a line of input.

Of course it is possible to handle a lot of this input parsing in Smalltalk,
and that is what the classes in package CommandShell do. This allows you
to do the input parsing directly without the assistance of a unix /bin/sh
program. In other words, in your example we would separate the lines of
input text, parse each line to interpret the tokens and expand file names
as if we were a unix shell, and then directly invoke each individual program
in each command line without the assistance of a real unix shell. And because
each program that you run ends with an exit status code that it provides to
the shell, we can make use of that exit status in Smalltalk, and do some
conditional processing just as might be done by the real /bin/sh program.


More information about the Squeak-dev mailing list