Tutorial - Shell Scripting


Candle provides some standard library functions to work with the shell of the native OS. And this is the most convenient, if not the only, way to communicate with the native OS, in Candle, at the moment.

There are several advantages of using Candle for shelling scripting than using traditional shell scripting languages, like Bash:
(Note: in current beta release, Candle's data model has not been extended to support files and directories. In Candle 1.0 formal release, you will be able to use path expressions on files and directories. One major advantage of shell scripting language over general-purpose scripting languages is its convenience in file selection. With the extended data model, Candle will be as convenient as, if not more than, traditional shell scripting languages.)

Calling a Shell Command

Below is a simple Candle script that calls the hostname command using candle:io:exec() library function:
method main() as string {
    candle:io:exec("hostname");  !! any other cmd that works on both Windows and Linux?
    return result();
On Windows, Candle starts a child process and executes the command line using Windows cmd.exe. On Linux, Candle calls C function popen(), which in turn invokes /bin/sh.

The output from the shell command is stored as Candle's standard method return value, and can be retrieved through the result() function.

And when the functions returns, the return value from the main method is automatically printed to STDOUT.

The second prototype of the candle:io:exec() function, accepts an additional parameter to tell Candle engine whether it should wait for the shell command to terminate before it returns. This can be useful if you just want to trigger a shell command without waiting for it to terminate, e.g.:
method main() {
    candle:io:exec("gedit");  !!gedit will not terminate until the editor is closed

Standard Input (STDIN) and Output (STDOUT)

To write to the standard output (STDOUT), you can call candle:io:writeln().  If you don't want a line break to be added to the output, you can call candle:io:write().
method main() as string {
    candle:io:writeln("writing a line of text to STDOUT.");
    candle:io:write("writing some text to STDOUT; ");
    candle:io:write("additional text on the same line.");

To read from the standard input, you can call candle:io:readln(). The input is stored as Candle's standard method return value, and can be retrieved through the result() function.
method main() as string {
    candle:io:write("Please enter a line of text:");
    return result();

Parsing the Command Output

If you want to process the output of the shell command in a structured manner, it can be easily done in Candle. You just need to write a grammar that matches the output, and use the xparse() function to turn the output into an AST (abstract syntax tree). Here's an example that converts the listing of ls command into an AST:

grammar ls-cmd-grammar {
    root = preface-line, line+;
    preface-line = "total", sp, digits, lf;
    sp = (" " | "&tb;")+;
    lf = "&lf;";
    line = file-type, permission, sp, link-cnt, sp, user, sp, group, sp,
        size, sp, month, sp, day, sp, time, sp, filename, lf;
    file-type = char;
    permission = ("r" | "w" | "x" | "-")+;
    link-cnt = digits;
    user = (char - sp)+;
    group = (char - sp)+;
    size = digits;
    month = letters;
    day = digits;
    time = digits, (":", digits)?;
    filename = (char - lf)+;

method main() as element {
    candle:io:exec("ls -la");
    let ls-output = xparse(result(), value::ls-cmd-grammar);
    return ls-output;
The AST can then be easily processed by Candle's advanced query features, e.g. transforming into a HTML table with highlighting.

As xparse() uses a grammar to do the pattern match, its performance can be much slower than simple string functions. So if you just want to do some simple value extraction or want good performance, you can use string functions to process the output or pipe the output to other text processing shell commands like grep or awk. xparse() should only be used when you want to process the entire output in a structured manner.