User Tools

Site Tools


exec_function

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

exec_function [2006/08/29 20:18] (current)
Line 1: Line 1:
 +# $EPIC: exec_function.txt,​v 1.4 2006/08/29 18:22:56 sthalik Exp $
 +======Synopsis:​======
 +$__exec__(<​program>​ <​args>​)
 +
 +======Technical:​======
 +   * If the <​program>​ argument is ommited, the empty string is returned.
 +   * Otherwise, <​program>​ is forked and executed with <​args>​ for arguments.
 +   * This function returns 3 (three) FDs, which are pipes to/from STDIN, STDOUT and STDERR of the forked process respectively.
 +   * The first FD returned (STDIN) is a write mode FD.
 +   * The subsequent FDs (STDOUT and STDERR) are read mode FDs.
 +   * The FDs returned can be used with $[[read]](),​ $[[write]](),​ etc, however...
 +   * Writing to a pipe that is full will cause epic to block.
 +   * Reading from a pipe that is empty will cause epic to block.
 +   * When epic is blocked waiting for a <​program>​ which is also blocked waiting for epic, deadlock will occur. ​ This is where the danger of this function lies.
 +   * The write pipes will remain full while <​program>​ is not emptying them by reading or closing them.
 +   * The read pipes will remain empty while <​program>​ is not filling them by writing or closing them.
 +   * The only guaranteed way to prevent deadlock is to examine the way the program reads and writes its input/​output and write your script to avoid the traps listed above.
 +   * This function is relatively new.  The /[[exec command|exec]] command may be better suited to your needs given the semantic difficulty of coding with this function.
 +   * Unlike the /[[exec command|exec]] command, <​program>​ will not be running under a shell. To emulate this feature, use $exec(sh -c "<​program>​ <​args>"​).
 +
 +======Practical:​======
 +This function offers an easier interface and a finer grain control
 +over the process than /[[exec command|exec]],​ but comes at the expense of being more
 +dangerous to use.
 +
 +You could use this function to implement real time text filtering or
 +scrambling using the unix tool "​sed"​ perhaps.
 +
 +$exec() might be a better alternative to $[[perl function|perl]]() and
 +$[[tcl function|tcl]]() for people who wish to distribute their scripts
 +and need a certain level of compatibility that the embedded languages
 +don't provide.
 +
 +======Returns:​======
 +One writable FD and two readable FDs.
 +
 +======Examples:​======
 +<​file>​
 +#
 +# An example of what we can expect.
 +#
 +
 +fe ($exec(tr a-zA-Z A-Za-z)) fd {
 +  echo $write($fd qwerASDF):​$read($fd):​$close($fd)
 +}
 +
 +#
 +# Output
 +#
 +
 +Out1: 9::0           # STDIN wrote 9 bytes (including NL), read failed,
 +Out2: -1:​QWERasdf:​0 ​ # STDOUT write failed, read 9 bytes,
 +Out3: -1::0          # STDERR write failed, read nothing,
 +
 +#  FD close succeeded in all cases.
 +
 +#
 +# Fortunately for us, md5sums i/o is very predictable. ​ It, like
 +# some other programs uses the EOF of its input as a signal to start
 +# delivering output, so it is always safe for $exec() so long as we
 +# do not attempt to read its output before closing its input.
 +#
 +# Note that the technique we use here can be used for most programs
 +# and all filters if we restrain ourselves from writing too much data
 +# to STDIN. Exactly how much data can be written depends on the "pipe
 +# size" ulimit which can be changed with the ulimit command in any(?)
 +# bourne compatible shell.
 +#
 +# Also note that in these examples, the FDs that aren't used are
 +# closed before processing begins rather than after. ​ It is
 +# important to do this because if the program tries to read/write
 +# a closed FD, it will always return immediately rather than
 +# blocking.
 +
 +alias md5 {
 +  # a fast way to set three local variables.
 +  fe ($exec(md5sum)) in out err {break}
 +  @ close($err)
 +  @ write($in $*)
 +  @ close($in)
 +  @ function_return = read($out)
 +  @ close($out)
 +}
 +
 +#
 +# Translate upper to lower and lower to upper using a perl script.
 +# The critical thing about this alias is the $|=1 in the perl script,
 +# which turns autoflush on so that we receive the output immediately
 +# after feeding it the input. ​ The gnu tr command cannot be made to
 +# do this, which makes it unsuitable for this application.
 +#
 +# Anyway, the reason this is critical is that we're going to $exec()
 +# the perl script outside of the alias, perhaps at bootup, and leave
 +# it running in the background forever with the assumption that every
 +# line we give it is going to give us precisely one line back.  Of
 +# course, this is a very delicate thing since errors are going to
 +# accumulate.
 +#
 +# Also, note the double quotes where we might normally use single
 +# quotes in the shell. ​ The input is broken into arguments according
 +# to epics "​word"​ rules. ​ There is no equivalent to the single quotes
 +# under these rules, though you can escape "​quoting hell" by wrapping
 +# the relevant parts of your script in braces like I have for this
 +# example.
 +#
 +
 +fe ($exec(perl -lpe "​BEGIN{$|=1};​{tr/​a-zA-Z/​A-Za-z/​}"​)) in out err {
 +  @ ::​transcase.in = in
 +  @ ::​transcase.out = out
 +  @ close($err)
 +  break
 +}
 +
 +alias transcase {
 +  @ write($transcase.in $*)
 +  return $read($transcase.out)
 +}
 +</​file>​
  
exec_function.txt ยท Last modified: 2006/08/29 20:18 (external edit)