Getting Started | Documentation | Glish | Learn More | Programming | Contact Us |
Version 1.9 Build 1556 |
|
Clients form the heart of the Glish system. They can be created in two ways. You can use either the predefined client function to execute a new instance of a client program (i.e., a program linked with the Glish Client Library; see Chapter 15, page ), or the predefined shell function to run an unmodified UNIX program as a simple type of Glish client. These two alternatives are discussed below.
The client function takes the name of a program and an optional set of arguments, invokes the program with the arguments, and returns a Glish agent value. You can then use this value to manipulate the program via events, as discussed in § 7.4, page , and § 7.5, page , above.
A call to client requires at least one argument, a string
giving the name of the program to execute. If the name begins with
a slash (/) or a period (.) then it is interpreted as
giving the complete pathname to the program; otherwise
system.path.bin.hostname is used to locate clients.
hostname in this case is the host on
which the client is being started. If no field matching the hostname
is found in system.path.bin, system.path.bin.default
is
used (if it exists). A field for the local host is created in
system.path.bin
at startup time using the $PATH environment
variable.
Additional arguments to client are converted to string values, if they are not already strings, and passed to the program. For example,
c := client("tester", 1:3, "hi")invokes the program tester and passes it two string arguments, ``
[1 2 3]
'' and ``hi''. The conversion of non-string
arguments to strings is done in the same mannar as when they are
printed. An element by element conversion can be done using as_string:
c := client("tester", as_string(1:3), "hi")would invoke tester with four arguments ``1'', ``2'', ``3'', and ``hi''. Starting tester with:
c := client("tester", 'hello there')would invoke tester with a single argument ``hello there'' whereas:
c := client("tester", "hello there")invokes tester with a two arguments ``hello'' and ``there''.
When a single multi-element string is supplied, the first element is taken to be the name of the program to invoke. The last example can also be written:
c := client("tester hello there")This invokes the program ``tester'', not ``tester hello there''. However,
c := client('tester hello there')fails because there is no program called ``tester hello there''.
The client function also takes a number of optional, named parameters:
c := client("tester", host="mars")runs tester on the remote host mars. If missing, the client runs on the local host.
\n
'):
c := client("tester", input="hello there")results in tester seeing a single line on its standard input, namely the string ``hello there'', while
c := client("tester", input='how\nare\nyou?')results in three lines appearing on tester's standard input.
Note that presently non-string values are converted to strings as though they were being printed, so
c := client("tester", input=1:3)results in the single line ``[1 2 3]'' appearing on the standard input. This may change in the future.
If no input= argument appears then the client inherits the Glish interpreter's standard input.
tester @ myhost, pid 18915: suspending ...indicating that the client tester running on host myhost with process ID 18915 has suspended. (See § 13.2, page , for more information on debugging suspended clients.)
For asynchronous clients, the client function still returns an agent value. The record associated with this value has its activate field set to a string giving special command-line arguments that need to be passed to the client. Once the client is executed by some means, these arguments will then be interpreted on the client's behalf by the Glish Client Library so that the client knows how to connect to the Glish interpreter.
a := client("special", async=T) print "executing special using:", a.activateassigns to a an agent value and then prints out the arguments that need to be used to invoke special and associate it with a's agent. Note that a host= option should not be used even if special is running on a remote host.
When the asynchronous client runs and ``joins" the current Glish script it generates an established event, just as do regular clients (see § 7.11, page , below). Even before this happens, though, you can send events to a and execute whenever statements for responding to a's events.
You can use the shell function to incorporate unmodified UNIX programs into a Glish program. As noted in § 4.10, page , above, a standard use of shell is to run a UNIX program, wait for it to terminate, and collect its output as a string vector. For example:
sources := shell("ls *.c")returns in sources a list of all of the files in the current directory that end in ``.c". The return status of the command which the shell executes is returned as an attribute:
print sources::statusto allow you to check on the exit status of the UNIX command.
Similar to client (see preceding section), shell takes a number of optional arguments. Of these, host=, input=, and ping= behave identically (suspend= is useful only for debugging Glish itself).
The async= option behaves differently, though. It runs the shell command asynchronously; that is, it instructs Glish not to wait for the command to complete but instead to use an event-oriented interface for the shell command. The asynchronous shell commands are called ``shell clients".
The asynchronous interface works as follows. First, when async=T is used, shell returns not a string value but instead an agent value, as client does. You then use the agent value to send stdin events to make text appear on the shell client's standard input, EOF events to close the standard input, and terminate events to terminate the client. Furthermore, each line of text the shell client writes to its standard output becomes a stdout event.
To illustrate, here's a Glish script that uses awk to print the numbers from 1 to 32 in hexadecimal, each appearing as a separate event:
cvt := "awk '{ printf(\"%x\\n\", $1) }'" hex := shell(cvt, async=T) count := 1 hex->stdin(count) whenever hex->stdout do { print count, "=", $value if ( count < 32 ) { count +:= 1 hex->stdin(count) } else hex->EOF() }
The first two statements associate an asynchronous shell client with the variable hex. The next line initializes the global count to 1 and sends that value to hex, making it appear on awk's standard input.
The whenever body prints out the current count and its hexadecimal equivalent, and then either increments the count and sends awk a new input line or closes its standard input.
You might think that a race exists between sending the first stdin event to hex's client and setting up the whenever to deal with the client's response. This problem does not arise, however, because the Glish interpreter does not read events generated by clients until it is done executing all of the statements in a script. (See § 13.1.2, page , below.)
One final note regarding asynchronous shell commands. Glish uses a technique called ``pseudo-ttys" for communicating with shell clients. This makes the shell clients' standard output be line-buffered (instead of block-buffered, the default). Without this technique, in the above example awk would buffer up its output until either it had filled an entire block (a lot of text) or its standard input was closed and it exited. In this case you would not immediately get a new stdout event for each stdin event, and the program would not work correctly. One drawback of using ``pseudo-ttys", though, is that the shell command truly believes that it is writing to a terminal. Programs that alter their behavior depending on whether they're writing to a terminal will engage the altered behavior. For example, on BSD systems the following Glish program:
a := shell("ls", async=T) whenever a->stdout do print $valueprints out several filenames at a time, because ls writes its output in columns when writing to a terminal.