exec Asynchronous execution of external programs |
||||||||||||||||||||||||
Usage |
||||||||||||||||||||||||
exec [switches] (<commandline:string>[,<magic data:variant>]) { <callback command> } |
||||||||||||||||||||||||
Description |
||||||||||||||||||||||||
Overview Executes the <commandline> by passing it to a command interpreter. The <commandline> is executed asynchronously: this means that when exec returns the control to the next command, <commandline> may be still running. The callback The <callback command> is triggered on several events related to the child process and it gets passed the following parameters: $0 = <event cause> $1 = <event parameter> $2 = <magic data> The first parameter specifies the event cause and contains one of the following strings: stdout, stderr, terminated, started and ping. By default (if no switches are used) only stdout type events are triggered. The second parameter depends on the event cause and contains data sensible to each event type. The third parameter is the eventual <magic data> passed to the exec command call. Interacting with the process If you use halt to terminate the callback then the slave process is killed immediately and no other callback events are triggered. If you return some non empty string then this string will be written to the process stdin stream. This trick can be used to control interactive processes. Please note that you must include all the relevant carriage returns and newlines in the return value (see $cr and $lf). Startup event If the -x switch is used then the startup event is triggered just after the process has been successfully launched. The $0 parameter passed to the callback contains the string started. Parameter $1 contains the pid of the slave process. Stdout data event The stdout data event is triggered when the process prints some output on its stdout stream. This event is triggered by default and to disable it you must use the -n switch. $0 contains the string stdout. If the -b switch is not used then $1 contains a single line of process output with the trailing carriage return and/or line feed stripped. If -b is used then $1 contains the whole process output block (eventually empty) with all the cr/lf pairs. Stderr data event The stderr data event is similar to the stdout one but there are three differences. The first one is that the stderr event is not triggered by default: you must use the -e switch to enable it. The second difference is that $0 contains stderr instead of stdout. The last difference is that $1 contains data coming from the slave process stderr stream. Termination event The termination event is triggered after the slave process has terminated its execution. You must use the -t switch to enable it since it is disabled by default. $0 contains the string terminated. $1 contains the process exit status value. (Note that if the process has crashed or has been terminated by an external signal then this value will be 0). Ping event The ping event is triggered only if the -p=<timeout> switch is passed. This event may be useful to monitor the process status while it is not emitting any output, to write data to its stdin stream (by the means of return) or simply to give some feedback to the user while the slave process is doing a long computation. The extended scope variables The <callback command> has a set of extended scope variables that conserve their value during the whole life time of the slave process. These variables can be accessed through the %:<varname> syntax and are useful to store process private data between multiple <callback command> calls. Some words about the switches If the -b switch is used then the <callback command> is called only once for the events stdout and stderr (if enabled) with the complete output block from the process. With the -b switch the events stdout and stderr are triggered once even if the process emits no output. The -s=<interpreter> switch may be used to specify the path of the command interpreter that is sh -c by default on UNIX machines and cmd.exe /c on Windows. The interpreter executable is searched on the system PATH. If the process can't be started then a warning message is printed in the current window unless the -q (quiet) flag is used. |
||||||||||||||||||||||||
Switches |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
Examples |
||||||||||||||||||||||||
# Really simple example: print only the stdout of a slave process exec("cat /proc/cpuinfo"){ echo $1; }; # Now print only stderr: enable stderr and disable stdout exec -e -n ("sed -senseless"){ echo $1; }; # Do it another way: enable stderr and filter out stdout exec -e ("sed -senseless"){ if($0 == "stderr")echo $1; } # Now enable all (almost) events and print them exec -e -t -x ("cat /proc/cpuinfo && sed -senseless"){ echo [event:$0] $1; } # Now see what happens if -b is used exec -b -e -t -x ("cat /proc/cpuinfo && sed -senseless"){ echo [event:$0] $1; } # Run an iterative script and kill it after 20 seconds exec -k=20000 ("while true; do sleep 1; echo \"Tic\"; done"){ echo [event:$0] $1; } # Run a blocking process, kill it after 20 seconds # and give feedback to the user by the means of ping exec -k=20000 -p=1000 -t ("cat") { if($0 == "ping")echo "[event:$0] Please wait while doing a huge computation ..." else if($0 == "terminated")echo "[event:$0] OK, done :)" } # Do the same but this time use the extended scope vars # Use also a nicer syntax exec -k=20000 -p=1000 -t ("cat") { switch($0) { case("ping"): { if(%:x == 1) { %:x = 0; echo "Tic!" } else { %:x = 1; echo "Tac!" } } break; case("terminated"): { echo "OK, done :)" } break; } } # Again do the same but kill the process explicitly exec -x -p=1000 -t ("cat") { switch($0) { case("started"): { # Initialize the counter %:x = 10; } break; case("ping"): { echo %:x %:x-- # When the counter reaches zero, kill the process with halt if(%:x == 0)halt; } break; case("terminated"): { echo "Boom!" } break; } } # Now play with an interactive process # WARNING: Please note that spam is illegal and generates bad karma # Try it only with your own e-mail address as recipient exec -s -k=60000 -t ("telnet my.mail.server.com 25") { if($0 == "started") { %:state = 0 # Returning an empty string does not write to stdin return } if($1 == "stderr") { echo "[stderr] $1" return } if($1 == "terminated") { echo "[process terminated]" return } echo "[stdout] $1" switch(%:state) { case(0): { # Waiting for 220 (ready) if($str.match("220*",$1)) { %:state++ echo "Sending HELO..." return "HELO myhostname$cr$lf"; } } break case(1): { # Waiting for 250 (after the HELO) if($str.match("250*",$1)) { %:state++ echo "Sending MAIL..." return "MAIL From: <myname@mydomain.com>$cr$lf" } else { echo "HELO command not accepted: $1" halt } } break; case(2): { # Waiting for another 250 (MAIL accepted) if($str.match("250*",$1)) { %:state++ echo "Sending RCPT..." return "RCPT To: <me@myself.org>$cr$lf" } else { echo "MAIL command not accepted: $1" halt } } break; case(3): { # Waiting for another 250 (RCPT accepted) if($str.match("250*",$1)) { %:state++ echo "Sending DATA..." return "DATA$cr$lf" } else { echo "RCPT not accepted: $1" halt } } break; case(4): { # Waiting for 354 (OK, go on) if($str.match("354*",$1)) { %:state++ echo "Sending body..." return "This is a test message :)$cr$lf$cr$lf.$cr$lf" } else { echo "Mail body not accepted: $1" halt } } break; case(5): { # We don't wait anymore :) %:state++ echo "Sending QUIT..." return "QUIT$cr$lf" } break; default: { # Usually the mail server closes the connection %:state++ if(%:state > 10) { # But if it does not in few messages # Then force the process to die halt } } } } | ||||||||||||||||||||||||