User Tools

Site Tools


# $EPIC: exec_function.txt,v 1.4 2006/08/29 18:22:56 sthalik Exp $


$exec(<program> <args>)


  • 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 may be better suited to your needs given the semantic difficulty of coding with this function.
  • Unlike the /exec command, <program> will not be running under a shell. To emulate this feature, use $exec(sh -c “<program> <args>”).


This function offers an easier interface and a finer grain control over the process than /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() and $tcl() for people who wish to distribute their scripts and need a certain level of compatibility that the embedded languages don't provide.


One writable FD and two readable FDs.


# 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 {
  @ = in
  @ ::transcase.out = out
  @ close($err)

alias transcase {
  @ write($ $*)
  return $read($transcase.out)
exec_function.txt · Last modified: 2006/08/29 20:18 (external edit)