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
-q | --quiet
    Quiet: do not print any warnings
-t | --trigger-termination
    Trigger the termination event
-x | --trigger-startup
    Trigger the startup event
-n | --no-stdout
    Do not trigger any stdout events
-e | --trigger-stderr
    Trigger stderr events
-b | --output-block
    Trigger the <callback command> with the stdout and stderr events exactly once,     passing the complete block of process output. The events are triggered even     if the process output is empty.
-k=<maximum run time> | --kill-after=<maximum run time>
    Kill the process unconditionally after <maximum run time> milliseconds.     If the -t switch is used then the termination event will be     triggered just after the process has been killed.
-p=<timeout> | --trigger-ping=<timeout>
    Trigger <callback command> with ping events every <timeout> milliseconds.
-w | --bind-to-window
    Kill the process if the current window is closed. In this case the     termination event is not triggered (since the parent window has been lost).     If this switch is not used then the process is rebound to     the active console window and continues running.
-s=<interpreter command> | --shell=<interpreter command>
    Use <interpreter command> instead of the default interpreter sh -c.     The <interpreter command> should be able to launch the interpreter     and should contain the necessary arguments in order to allow     KVIrc to pass the commandline by appending it as the last parameter.
-d | --direct
    Use no command interpreter at all: run the command directly.     Takes precedence over -s.
-q | --quiet
    Run quietly
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
            }
        }
    }
}

Index, Commands