# $EPIC: exec_function.txt,v 1.4 2006/08/29 18:22:56 sthalik Exp $ ======Synopsis:====== $__exec__( ) ======Technical:====== * If the argument is ommited, the empty string is returned. * Otherwise, is forked and executed with 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 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 is not emptying them by reading or closing them. * The read pipes will remain empty while 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, will not be running under a shell. To emulate this feature, use $exec(sh -c " "). ======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:====== # # 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) }