Language basics

This is a quick introduction to XSharper as a scripting language, with more samples than text.

Basics

Structure

Structure of XSharper program is

<?xml version="1.0" encoding="utf-8"?>
<xsharper xmlns="http://www.xsharper.com/schemas/1.0" >
        <action id="action1">
        <action id="action2">
        <action>
        <action>
        ....
</xsharper>

Using XML immediately makes IntelliSense live in Visual Studio 2008: open .xsh file as XML, assign scheme to http://www.xsharper.com/schemas/1.0 , and Visual Studio will suggest attribute names, possible enumeration values and more.

To make developer's life easier, a few additional liberties are allowed:

For example, "Hello world" program can be written as

<Print>Hello, world!</Print>

or even

<PRINT value="Hello, world!" />

As you see above, value attribute and element text are usually interchangeable.

Variables

XSharper has concept of a variable: an object with case-insensitive name attached to it.

Variable name may contain spaces and special characters. It cannot, however, start with space, number, single/double/backquote, minus, plus, ~ or %. It can be an empty string though.

To set a variable:

<set name="x" value="Hello" />

Empty variable names are allowed:

<set name="" value="Value of empty" />

Variable with names that start with % used to access environment variables:

<set name="%ENV%" value='New value' />

To make the XML more readable, two syntaxes below are equivalent:

<set name="x">Hello</set>
<set x="Hello" / >

To unset a variable, skip the value attribute:

<set name="x" />

An attempt to access a unset variable produces an exception.

Transformations

One of the core features of XSharper is support for text transformations, which are controlled by transform (or, shortened, tr) attribute of almost all actions.

This attribute controls:

<!-- PRINT.XSH -->
<print tr="trim squareToAngle">
	[xml]I hate writing all these &gt; and &lt; but sometimes it's easier to use square brackets and replace them with angle[/xml]
</print>

<set firstName="Michael" />
<set lastName="Parker" />
<print>----BEGIN --</print>
<print tr="trim multiLine expandSquare">

	Dear [firstName] [LastNAME],

	Best regards,
	XSharper development team

</print>
<print>----END --</print>

Default transformation is Expand, i.e. expand multi-expressions formatted as ${multi}

Another note is that transformation applies to all text attributes of an action, including the value. It's possible to explicitly exclude the value by setting verbatim="true" attribute.

<set text="Michael" />
<!-- Prints Name is: Michael -->
<print tr="expand">Name is: ${text}</print>

<!-- Prints Name is: ${text} -->
<print tr="expand" verbatim="true">Name is: ${text}</print>

Complete list of transformations can be obtained as

C:\>xsharper /? transformRules                                                                                                                                                                        
XSharper v.0.9.1243.0 DeltaX Inc. Copyright (c) 2006-2010

enum TransformRules (XSharper.Core.TransformRules)

Enum values:
  None                  0x0000000000 No expansion
  Expand                0x0000000001 Expand ${variables}
  ExpandDual            0x0000000003 Expand variables formatted as ${{var}}
  ExpandSquare          0x0000000005 Expand variables formatted as [var]
  ExpandDualSquare      0x0000000009 Expand variables formatted as [[var]]
  ExpandReplaceOnly     0x0000000011 Do replacements only in expanded text
  ExpandTrimOnly        0x0000000021 Trim expanded text only
  ExpandAfterTrim       0x0000000081 Trim first, expand later
  TrimBeforeExpand      0x0000000081 Trim first, expand later
  ExpandMask            0x00000000ff Expand flags mask
  TrimStart             0x0000000100 Trim whitespace at the beginning of the string
  TrimEnd               0x0000000200 Trim whitespace at the end of the string
  Trim                  0x0000000300 Trim whitespace at the beginning and at the end of the string
  TrimInternal          0x0000000400 Trim internal whitespace
  Multiline             0x0000000800 Treat the string as multiline, and do processing of each line
  TrimMask              0x0000000f00 All trim flags mask
  TildaToSpace          0x0000010000 Replace ~ with space
  BackqToDouble         0x0000020000 Replace ` with "
  CurvedToAngle         0x0000040000 Replace { with < , and } with >
  SquareToAngle         0x0000080000 Replace [ with < , and ] with >
  NewLineToLF           0x0000100000 Replace new lines with a single LF
  NewLineToCRLF         0x0000200000 Replace new line with CR LF
  DoubleSingleQuotes    0x0000400000 Convert ' to '' (useful for SQL)
  DoubleDoubleQuotes    0x0000800000 Convert " to "" (useful for C#)
  EscapeXml             0x0001000000 Escape all XML special characters
  QuoteArg              0x0002000000 If the string contains spaces, wrap it in double quotes. Internal double quotes are replaced with "
  EscapeRegex           0x0004000000 Escape all special regex characters
  RemoveControl         0x0008000000 Replace all non-ASCII characters with . (dot)
  UnescapeC             0x0010000000 Unescape C# strings, e.g. \n => newline
  EscapeC               0x0020000000 Escape C# strings, e.g. newline character => \n etc
  TabToSpaces2          0x0040000000 Tabs to spaces (counting tab as 2 spaces)
  TabToSpaces4          0x0080000000 Tabs to spaces (counting tab as 4 spaces)
  TabToSpaces8          0x00c0000000 Tabs to spaces (counting tab as 8 spaces)
  ReplaceMask           0x00ffff0000 All replace flags
  Default               0x0000000001 Default transformation

Multi-Expressions

To access a value of a variable, XSharper transformations are used:

<!-- Print Hello world! -->
<set name="x" value="Hello" />
<print>${x} world!</print>

Multi-expression is a piece of text included between the special characters ${ } (${{...}} or [[ ... ]] may be used instead, as described below ).

The word "multi-" means that several expressions may be concatenated with | and the first set value is chosen.

<!-- Prints TestB because variable a is not defined and b is defined -->
<set name="b" value="TestB" />
<set name="c" value="TestC" />
<print>${a|b|c}</print>

There are different types of expressions, and the expression type is determined by the first character of an expression as explained below.

Calculated expressions

If first character of an expression is = , it is interpreted using rules close to those of C#, although much more forgiving in conversions between types.

For example (int)"20d" will throw an exception in normal C#, yet will produce a double value of 20.0 in XSharper calculated expression.

<!-- EXP1.XSH will print 
	Type of 'num' is System.String 
	Type of 'text' is System.String
	Expression value=52 
	ArrayLength=3
	'aa' is larger than 'bb'=False
	C:\Windows has 226 files
-->
<set num="20" />
<set text="Hello" />
<print>Type of 'num' is ${=$num.GetType().FullName}</print>
<print>Type of 'text' is ${=$text.GetType().FullName}</print>
<print>Expression value=${= 2*$num+($text.Length+1)*2}</print>
<print>ArrayLength=${= new int[] { 1,2,3}.Length }</print>
<print>'aa' is larger than 'bb'=${= 'aa' #GT# 'bb' }</print>

Some notable differences from C# syntax:

Numbers

If an expression is a valid number, it is the value of the expression.

<!-- prints 2000 -->
<print>${2e3}</print>

Quoted strings

If an expression starts with ', or ", or `, the expression is treated as string (expecting a matching character at the end).

<!-- prints Michael Jackson -->
<print>Michael ${'Jackson'}</print>

Environment variables

If the first character is %, environment variable is used:

<!-- EXP2.XSH will print 
		command interpreter=C:\Windows\system32\cmd.exe
		home drive=C:
		xsh_path=[NULL]
-->
<print tr='expand trim multiline'>
	command interpreter=${%COMSPEC%}
	home drive=${%HOMEDRIVE%}
	xsh_path=${%XSH_PATH%|'[NULL]'}
</print>

By default, environment variables of the current process are accessed. To access variables of the current user or machine, user: and machine: prefixes are used instead. For example:

<print>System PATH=${%machine:PATH%}</print>

Variable name

If all the previous checks fail, the value is treated as variable name. Empty variable names are also allowed:

<!-- Prints The desert is Tiramisu, as the variable desert is not set -->
<set name="" value="Tiramisu" />
<print>The desert is ${desert|}</print>

<eval> action

Multiple expressions may be interpreted in sequence using action:

<eval>
	c.WriteLine(`Length of AAA `+'AAA'.length);
	c.WriteLine("Length of BB "+'BB'.length);
	c.Set("a",20);
	c.Print("A=${a}");
</eval>

The script below makes a screenshot and saves it to scrshot.png:

<reference name="System.Windows.Forms" addUsing="true" />
<eval>	c.Set('v',SystemInformation.VirtualScreen);
		c.Set('bmp',new Drawing.Bitmap($v.Width,$v.Height));
		c.Set('g',Drawing.Graphics.FromImage($bmp));
		$g.CopyFromScreen( new Drawing.Point(0,0), Drawing.Point.Empty, $bmp.Size);
		$bmp.Save('scrshot.png');
</eval>

Comments and blocks

Comments

For comments the usual XML comments <!-- --> may be used, or rem action:

<!-- this is a comment -->
<rem>This is also a comment, particularly useful 
if you need to insert --> into it</rem>

Blocks

There are basic loops, blocks, and conditionals in XSharper. But with C# behind it, you can also use any construction C# allows.

The most primitive structure is sequence, which is just a sequence of commands:

<xsharper>
	<sequence>
		<print>This is line 1</print>
		<print>This is line 2</print>
	</sequence>
</xsharper>

Block is a special sequence, which can be followed by the standard try/catch/finally:

<block>
	<print>I'm about to open a file that may not exist</print>
	<try>
		<readText from="c:\text.txt" outTo="txt" /> 
	</try>
	<catch>
		<print>Exception ${=c.CurrentException.Message} has occurred</print>
		<throw />
	</catch>
	<finally>
		<print>This is executed no matter what</print>
	</finally>
</block>

Throwing exceptions

Exceptions may be thrown as

<throw class="IOException">Failed to open file</throw>

If class is not specified, exception of type ScriptUserException is thrown:

<throw>User error</throw>

There is a special exception type ScriptTerminateException which terminates the execution of the current script with error code. Unlike other exceptions, it is rethrown at the end of catch in blocks, making it similar to ThreadAbortedException in .NET:

<throw class="XS.ScriptTerminateException">5</throw>

This is useful for premature script termination, and there is shorter notation:

<exit exitCode="2" />

Error message can be produced too

<exit exitCode="-5">Failed to open file!</exit>

Conditions and loops

If/Else

XSharper has the usual if/else construction, which has two notations.

The first XML-like notation, where else is included as a part of if element is a bit cumbersome. There is however an alternative syntax, with , yet there is no way to validate it against XSD schema.

<set a='Z' />
<set b='X' />
<if isTrue="${=$a>$b}">
		<print>${a} is more than ${b}</print>
	<else>
		<print>${a} is NOT more than ${b}</print>
	</else>
</if>

<!-- alternative syntax -->
<if isTrue="${=$b>$a}">
		<print>${b} is more than ${a}</print>
<else />
		<print>${b} is NOT more than ${a}</print>
</if>

There is a bunch of possible attributes in conditionals, isTrue, isFalse, isSet (if variable is set), isNotSet, isZero, isNotZero etc. Use xsharper /? if for more details.

While loop

XSharper has only a simple while loop.

<xsharper>
	<set i="0" />
	<while isTrue="${= $i #LT# 5 }">
		<print>i=${i}</print>
		<set i="${=(int)$i+1}" />
	</while>
</xsharper>

Note the use of #lt# instead of &lt;, to make the expression more readable.

While loop may define a maximum number of loops:

<xsharper>
	<set i="0" />
	<while maxLoops="3">
		<print>i=${i}</print>
		<set i="${=(int)$i+1}" />
	</while>
</xsharper>

Actually, while can automatically assign its loop counter value to a variable specified in name attribute, making the code a bit shorter:

<while name="i" maxLoops="3" >
	<print>i=${i}</print>
</while>

There is also a break statement, to exit a loop prematurely:

<xsharper>
	<set i="0" />
	<while>
		<print>i=${i}</print>
		<set i="${=(int)$i+1}" />
		<if isTrue="${= $i > 10}">
			<break />
		</if>
	</while>
</xsharper>

ForEach loop

forEach loop iterates through each element of a collection, XML document or a rowset.

Syntax is <foreach variable='${enumerable}'>

<!-- print 1,2,3 in different rows -->
<foreach x="${= {1,2,3}}" >
	<print>${x}</print>
</foreach>

Or <foreach name='x' in='${enumerable}'> where name is optional (and defaults to empty string):

<!-- print list of files in C:\ -->
<foreach in="${= new DirectoryInfo('c:\').GetFileSystemInfos('*.*') }" >
	<print>${=$.FullName}</print>
</foreach>

foreach can also parse rowsets:

	<!-- running a piece of code for every row of rowset -->
	<rowset id="rs">
		<row order="1" description="Two large pineapple pizzas" />
		<row order="22" description="One Greek Salad" />
	</rowset>
	
	<foreach rowsetId="rs" name="pref_">
		<print>Order: ${=$pref_order.PadLeft(5)}</print>
		<print>Description=${pref_description}</print>
		<print>-----</print>
	</foreach>

Subroutines and scripts

XSharper has a concept of a subroutine, that accepts 0 or more arguments and returns a single object value.

<!-- SUB1.XSH. Calculate squares of 20 and 50 -->
<xsharper>
	<set a="${=20}" />
	<call subId="square" outTo="result">
		<param>${a}</param>
	</call>
	<print>${a}*${a} = ${result}</print>
	<print>50*50 = ${=square(50)}</print>
	
	<!-- Subroutines -->
	<sub id="square">
		<param name="n" required="true" />
		<set ret="${=$n*$n}" />
		<return>${ret}</return>
	</sub>
</xsharper>

As seen above, subroutines may be invoked directly from the expressions.

To execute square subroutine from another script, it's possible to use include action:

<!-- SUB2.XSH. Calculate square of a value specified as a command-line argument -->
<xsharper>
	<include from="sub1.xsh" />
	<param name="v" required="true">Value to calculate square</param>

	<print>${v}*${v} = ${=square($v)}</print>
</xsharper>

Finally, execute another script as if it was executed from command line:

<!-- SUB3.XSH. Print square of a value using SUB2.XSH -->
<xsharper>
	<exec from="sub2.xsh">
		<param>500</param>
	</exec>
</xsharper>

Isolation level

By default, subroutines have read-only access to the variables of the caller and any set or changed variables in the subroutine body are restored.

<set x="50" />
<print>Before call x=${x}</print>
<call subId="sub" isolation="default" />
<print>After call x=${x}</print>

<sub id="sub">
	<print>Sub. Initially x=${x|'Undefined'}.</print>
	<set x="100" />		
	<print>Sub. Later x=${x}.</print>
</sub>

The code above will print:
Before call x=50
Sub. Initially x=50.
Sub. Later x=100.
After call x=50

This behaviour can be changed by caller, by changing attribute isolation

With isolation="none", when no variables are restored, the output will be

Before call x=50
Sub. Initially x=50.
Sub. Later x=100.
After call x=100

With isolation="high", the subroutine will have a totally clean variable list, w/o access to the caller variables at all:

Before call x=50
Sub. Initially x=Undefined.
Sub. Later x=100.
After call x=50

Using C#

Code snippets

While XSharper has basic primitives like expressions, or if/else, or while loop, these are often not sufficient. Instead of reinventing the wheel, it is very easy to embed C# code into the script.

<xsharper>
	<code>
		Console.WriteLine("Hello, world!")
	</code>
</xsharper>

Everything inside <code> is a single class, so local variables defined in one code block are not accessible in another.

It's also possible to mix & match too by inserting actions into C# code. And even add methods to the class written in C#.

<!-- CODE-MIX-AND-MATCH.XSH -->
<xsharper>
	<code>
		Console.WriteLine("This is a header!");
		<print>This is line #2</print>

		for (int i=0;5>i;++i)
		{
			c["x2"]=square(i);
			c.Write(i+"^2");
			<print>=${x2}</print>
		}

		<sub id="square">
			<param name="n" />
			<return>${=$n*$n}</return>
		</sub>
	</code>
</xsharper>

Some interesting notes:

However, when inserting larger pieces of C# code, perhaps copy-pasted from somewhere, XML escaping becomes rather annoying. There are two possible solutions.

CDATA section:

<code><![CDATA[[
	for (int i=0; i<5;++i)
		c.WriteLine("i="+i);
]]></code>

or an XML processing instruction:

<?code
	for (int i=0; i<5;++i)
		c.WriteLine("i="+i);
?>

Which can be further shortened from <?code to <?_:

<?_
	for (int i=0; i<5;++i)
		c.WriteLine("i="+i);
?>

Headers

As everything inside <code> is inserted by XSharper into a class, it's not possible to define complete classes to be shared with different <code> actions, or define namespaces.

To insert code outside of any class or execution sequence, there is a <header> action, which may be also inserted as <?header or <?h processing instruction.

<!-- prints
	MD5('The quick brown fox jumps over the lazy dog.') = 'e4d909c290d0fb1ca068ffaddf22cbd0'
-->
<?h 
	using System.Security.Cryptography; 

	public static class Helper 
	{
		public static string md5 (byte[] data)
		{
			using (HashAlgorithm h=(HashAlgorithm)MD5.Create())
				return XS.Utils.ToHex(h.ComputeHash(data));
		}
	}
?>
<set str="The quick brown fox jumps over the lazy dog." />
<print value="MD5('${str}')='${=Helper.MD5(Encoding.ASCII.GetBytes($str))}'"/>

References

To add a reference to another assembly, there is a <reference> action. Assemblies may be referenced by name (using name attribute) or by location (using from attribute)

<reference name="System.Windows.Forms" />
<?h using WF= System.Windows.Forms; ?>
<eval>
	WF.MessageBox.Show("Hello, world");
</eval>

or a bit shorter:

<reference name="System.Windows.Forms" addUsing="true" />
<eval>
	MessageBox.Show("Hello, world");
</eval>

Parsing command line

XSharper scripts accept command line parameters. These parameters are declaratively defined, automatically parsed and values assigned to XSharper variables. The same declaration is also used to display script usage information.

<xsharper>
        
	<param name="from" required="true" >Copy from file</param>
	<param name="to" required="true" >Copy to file</param>
	<param switch="overwrite" count="none"	default="false" unspecified="true" 
		tr="trim trimInternal">	
			Specify this parameter if you need files to 
			be overwritten. This text is deliberately verbose 
			to demonstrate wrapping.</param>
	<usage options="default ifHelp ifNoArguments" exitcode="1" />

	<!-- Set overwrite attribute of copy action to always or never -->
	<setattr	actionId="cp" 
				name="overwrite">${=$overwrite?'always':'never'}</setattr>

	<!-- Do copy -->				
	<set count="0" />
	<copy id="cp" from="${from}" to="${to}">
		<set count="${=(int)$count+1}" />
	</copy>
	
	<print>${count} files copied</print>

</xsharper>

At the beginning three parameters are defined. First two arguments will be assigned to from and to variables, correspondingly. The third parameter, which is optional, is a switch /overwrite which does not have any values following it (count="none"), and sets variable overwrite to true if specified (default is false).

If executed without parameters, because usage options are set to ifNoArguments the script outputs

Copy file utility

Usage: COPY1 from to [parameters]

  from          Copy from file
  to            Copy to file
  /overwrite    Specify this parameter if you need files to be overwritten.
                This text is deliberately verbose to demonstrate wrapping.

and exits with exit code "1". Long text lines are automatically wrapped depending on console window width.

Add GUI to the mix

Command line parameters a great for something that is used often. For less frequently used tools it's too hard to read the documentation first, figure out the right combination of switches through trial and error, deal with escaping and so on.

Fortunately, with XSharper scripts GUI is just one click away (the look is completely customizeable, #/gui-param is just another XSharper script in Library ):

C:\> xsharper #/gui-param COPY1.xsh

produces

Custom usage

While automatic usage generation is useful, it is also possible to replace it with completely custom text instead. For example, the script below displays custom usage text (by adding a
w/o name or switch attributes), and parameter parsing is handled separately (parameters have empty text, and thus not displayed in usage):

<xsharper>
	<param tr="trim multiline"><![CDATA[
		Usage: Copy <from-file> <to-file> [/overwrite]
		
		Copy file from one location to another. By default it skips existing files.
	]]></param>
	<param name="from" description="&lt;from-file&gt;" required="true"/>
	<param name="to" description="&lt;to-file&gt;" required="true" />
	<usage options="ifNoArguments ifHelp" exitcode="1" />
...

Note that using description attribute it's possible to define a user friendly name of a parameter that does not match the variable being set.

Output and text files

Print

As can be seen in previous examples, the core of XSharper output is <print> action, which outputs a string to console.

To make output a bit nicer, mimic operating system streams, and also categorize output, there are 5 output options.

Name Purpose Default direction
^out Normal output Standard output
^bold More visible output, like section names etc. Standard output
^info Optional output Standard output, unless //quiet is specified
^error Error messages Standard error
^debug Debug messages None. Debug output (can be seen with dbgview utility) if //debug switch is specified. Also copied to standard output, if //debugc switch is specified
^nul,^null No output

In many actions it's possible to specify where the output goes via outTo attribute.

<print>This goes to standard output</print>
<print outTo="^bold">This is bold</print>
<print outTo="^info">This won't be shown if executed with //quiet</print>
<print outTo="^debug">This is debug output</print>
<print outTo="^error">This is error</print>

Also the same outTo may redirect the output to a variable

<print outTo="food">Macaroni and cheese</print>
<print>Today's special is ${food}</print>

also if variable name is preceded by +, value is appended to the variable instead of overwriting it

<print outTo="food">Macaroni</print>
<print outTo="+food"> and cheese</print>
<print>Today's special is ${food}</print>

Text file may be used for output using ^# prefix. Also, append is possible by using +^# prefix. Also, after | encoding may be specified (default to UTF8 with BOM)

<print outTo="^#c:\hello.bat|ascii">echo Hello, world</print>
<shell>c:\hello.bat</shell>

By default, print appends a new line to every string output, that may be switched off using newline="false" attribute:

<print newline="false">This is normal.</print>
<print newline="false">This is bold</print>
<print>This is normal again<print> 

Redirection

Output to one stream may be redirected to another:

<redirect outTo="^error">
	<print>This goes to error</print>
</redirect>

Or to a file (in UTF8 w/o BOM):

<set filename="output.txt|utf8/nobom" />
<redirect outTo="^#${filename}">
	<print>Line1</print>
	<print>Line2</print>
</redirect>

Or can add + to append:

<set filename="output.txt|utf8/nobom" />
<redirect outTo="+^#${filename}">
	<print>Line3</print>
	<print>Line4</print>
</redirect>

ReadText

Reading a text file into a variable is easy.

<readtext from="file.txt" outTo="x" />

Or just can out it to console instead:

<readtext from="file.txt" outTo="^out" />

If it's known in advance that the file is in UTF8

<readtext from="file.txt|utf8" outTo="x" />

or

<readtext from="file.txt " outTo="x" encoding="utf8" />

Or, if the file is on the web-server:

<readtext from="http://www.ncjrs.gov/txtfiles/victcost.txt" outTo="x" />

Finally, a multi-expression can be used as well (although XML often better demonstrates writer's intentions)

<set x="${= c.ReadText('http://www.ncjrs.gov/txtfiles/victcost.txt')}" />

WriteText

Writing a text file is just as easy

<set txt="Hello, world" />
<writeText to="c:\file.txt" encoding="utf8/nobom" />

To save as UTF16 with byte order mark:

<set txt="Hello, world" />
<writeText to="c:\file.txt" encoding="utf16/bom" />

Or, can just use print instead

<set txt="Hello, world" />
<print newline='false' outTo='^#c:\file.txt|utf16/bom'>${x}</print>

Data islands

Scripts often don't have the same level of data and logic separation as programs in "traditional" languages. And this is for a good reason: logic in scripts changes about as often as the data being processed (and if it's not true, "traditional" compiled programming language, with type validation etc, would be, arguably, a better choice).

So a scripting language needs a way to hardcode data into the script itself in text, XML, or CSV. Ideally, it should be as easy to use external files too. CSV support may be surprising if there is XML, but I often find CSV and text a lot easier to read and modify than XML because of reduced verbosity.

For example,

Product, 	Count, 	Price
Cheese,		2,	4.99
Meat,		4,	9.99
Cereal,		9,	2.49

is a lot more readable than

<data>
	<product>
		<name>Cheese</name>
		<count>4</count>
		<price>4.99</price>
	</product>
	<product>
		<name>Meat</name>
		<count>4</count>
		<price>9.99</price>
	</product>
	<product>
		<name>Cereal</name>
		<count>9</count>
		<price>2.49</price>
	</product>
</data>

Rowset

More often than not when writing scripts there is a need to deal with flat lists of objects, not object hierarchies. Could be lists of files, SQL tables and so on.

To represent this type of data, there is rowset action, which contains multiple rows in it. Representation may different, whatever is more convenient to the writer.

For example, XML using a special action to for every record, with attributes being values.

<rowset id="data">
	<row name="Cheese" count="2" price="4.99" />
	<row name="Meat" count="4" price="9.99" />
	<row name="Cereal" count="9" price="2.49" />
</rowset>
<print>${=$~data.ToTextTable()}</print>

or "normal" XML using elements

<rowset id="data">
	<row><name>Cheese</name><count>2</count><price>4.99</price></row>
	<row><name>Meat</name><count>4</count><price>9.99</price></row>
	<row><name>Cereal</name><count>9</count><price>2.49</price></row>
</rowset>
<print>${=$~data.ToTextTable()}</print>

or CSV using comma or other column separator

<rowset id="data" csvOptions="default header ignoreEmpty">
	name, 		count, 	price,

	Cheese,		2,		4.99
	Meat,		4,		9.99
	Cereal,		9,		2.49
</rowset>
<print>${=$~data.ToTextTable()}</print>

All these notations are equivalent. The final line prints the rowset nicely as a table:

+--------+-------+-------+
| name   | count | price |
+--------+-------+-------+
| Cheese | 2     | 4.99  |
| Meat   | 4     | 9.99  |
| Cereal | 9     | 2.49  |
+--------+-------+-------+

Rowset may also be converted to a .NET DataTable using .ToDataTable() method.

By default columns are treated as text, but type may be explicitly specified. Also one rowset may be derived from another rowset with filtering and sorting. For example, the below prints total cost of cereal ( 2.49 * 9 = 21.41 ). For comparison, cost of meat is calculated using C# snippet with LINQ (this one requires .NET 3.0).

<!-- Create columns with types explicitly specified -->
<rowset id="data">
        <col name="name" type="string" />
        <col name="count" type="int" />
        <col name="price" type="decimal" />
       Cheese,		2,		4.99
	Meat,		4,		9.99
	Cereal,		9,		2.49
</rowset>

<!-- Create a new rowset, with only one row for cereal  -->
<rowset id='data-cereal' rowsetId='data' >
	<where isTrue='${=$name=="Cereal"}' />
</rowset>

<!-- Calculate total of that row -->
<foreach rowsetId="data-cereal">
	<set total='${=	$price * $count }' />
</foreach>

<!-- Print total cost of cereal -->
<print>Total cost of cereal is ${total|0}</print>

<?_
	var meat=(from r in c.Find<XS.RowSet>("data",true)
		  where (string)r["name"]=="Meat"
		  select r.GetInt("count")*r.GetDecimal("price")).Single();
	c.WriteLine("Total cost of meat is "+meat);
?>

Xml document

There is also a basic support for XML data islands, to represent hierarchical structures.

With the following definition

<xmldoc id="x1">
	<html xmlns="">
		<head />
		<body>
			<p id="par1">
				<b>Hello</b>, world!
			</p>
			<p id="par2">
				<i>Quick brown</i>, fox!
			</p>
		</body>
	</html>
</xmldoc>

The code below prints inner XML of paragraph par1

<print tr="trim expand">${= $~x1['//p[@id="par1"]'].InnerXml }</print>

This prints the first bold text in par1:

<print>${= $~x1.XValue('//p[@id="par1"]/b[1]/text()') }</print>

The data can be also accessed from C# code, using the usual XML parsing tools from .NET 2.0:

<?_ 
	// Find XmlDoc named x1 (throw exception if not found)
	XS.XmlDoc x=c.Find<XS.XmlDoc>("x1",true); 

	x["/html/body/p[@id='par1']"].InnerXml="<b>Good bye!</b>";
	c.WriteLine(x.XmlDocument.OuterXml);
?>

Finally, if executed on machine with .NET3, LINQ for XML can be used:

<?_
	var x=XDocument.Parse( c.Find<XS.XmlDoc>("x1",true).OuterXml); 
	c.Dump(x.Root.Descendants("p").ToArray());
?>

Also, the data may be loaded from file instead of being specified directly:

<xmldoc id="z" from="data.xml" />

There are many other possibilities, including conversions between XmlDoc/RowSet/.NET DataTable/text table/CSV format/XML etc.

Databases

XML data islands and rowsets are handy for small amount of data. Handling larger datasets requires proper database support. In particular, I needed XSharper to install & initialize MS SQL databases (any other database with a .NET client may be used with client factor class name specified through factory attribute of database action).

The samples below assume that there is a default SQLExpress installation on the machine.

Creating a database:

<database cs="Data Source=.\SQLEXPRESS; Initial Catalog=master;Integrated Security=True;" >
	<sql>CREATE DATABASE XTest</sql>
	<print>Database created</print>
</database>

Adding a table:

<database cs="Data Source=.\SQLEXPRESS; Initial Catalog=XTest;Integrated Security=True;" >
	<sql>
		create table Customers (
			customerId int identity(1,1) not null primary key,
			firstName nvarchar(50),
			lastName	nvarchar(50),
			address	nvarchar(50)
		)
	 </sql>
	<print>Table Customers created</print>
</database>

In Oracle case, this would look like

<database cs="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=LOCALHOST)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));User Id=system;Password=123;" factory="Oracle.DataAccess.Client">
<sql>create table info (
	mother varchar2(100),
	child varchar2(100),
	bdob timestamp
	)</sql>
</database>

Please note that each statement is executed separately as a command, "GO" command from MS SQL batches is not currently allowed. Separate SQL statements need to be executed as separate <sql> actions.

Inserting records.

The usual INSERT SQL is always an option, but I find its syntax rather difficult to read or change (only in MS SQL 2008 there are some notable improvements). Instead there is sqlinsert action, which is similar to rowset and may contain CSV inside:

<database cs="Data Source=.\SQLEXPRESS; Initial Catalog=XTest;Integrated Security=True;" >
	<sqlinsert table="customers" csvColumns="firstName,lastName,address">
		John, Smith, "32 Thornbush Court, Toronto, Ontario"
		Michael, White, "12/22 Yonge street, Toronto, Ontario"
		Melissa, Brown, "52 Parlament ave., Toronto, Ontario"
	</sqlinsert>
</database>

Getting the data back

This is easy:

<database cs="Data Source=.\SQLEXPRESS; Initial Catalog=XTest;Integrated Security=True;" >
	<sql outTo="^out" type="query">Select * from Customers</sql>
</database>		

produces

+------------+-----------+----------+--------------------------------------+
| customerId | firstName | lastName | address                              |
+------------+-----------+----------+--------------------------------------+
|          1 | John      | Smith    | 32 Thornbush Court, Toronto, Ontario |
|          2 | Michael   | White    | 12/22 Yonge street, Toronto, Ontario |
|          3 | Melissa   | Brown    | 52 Parlament ave., Toronto, Ontario  |
+------------+-----------+----------+--------------------------------------+

There are many attributes of sql action that can be used, for example, to save the data to a rowset for future processing using toRowsetId attribute, and so on.

Upsert

sqlinsert is getting particularly useful for "upsert", add a record only if no record already exists, and update otherwise. T-SQL verbosity in this case just hits the roof. In XSharper only a new field keys is added, to specify which columns are used to find the existing record.

For example, change address of John Smith to "50 Blackberry Rd, Ottawa, ON", if he does exist, and insert the record otherwise:

<database cs="Data Source=.\SQLEXPRESS; Initial Catalog=XTest;Integrated Security=True;" >
	<rowset id="data" csvColumns="firstName|lastName|address" csvSeparator='|'>
		John| Smith|50 Blackberry Rd, Ottawa, Ontario
	</rowset>
	
	<sqlinsert 	table="customers"  
					rowsetId="data" updateMode="mssql" 
					keys="FirstName,lastName"  />

	<print outTo="^bold">Result:</print>
	<sql outTo="^out" mode="query">Select * from Customers</sql>
</database>

Generated syntax depends on the updateMode, with simple and msSql choices doing the usual "if (exists) update() else insert()", which is potentially unsafe if used outside of transaction with serializable isolation level (but probably fine more most scripts). Of the two, msSQL is a bit faster, and generates something like

UPDATE [customers]
SET [address]='50 Blackberry Rd, Ottawa, Ontario'
WHERE ([FirstName] = 'John') AND ([lastName] = 'Smith')
IF @@ROWCOUNT=0
INSERT INTO [customers] ( [firstName], [lastName], [address] )
SELECT 'John','Smith','50 Blackberry Rd, Ottawa, Ontario'

There are also merge option invoking the new SQL MERGE statement, requiring MS-SQL 2008. To get XSharper to autodetect the database updateMode can be set to auto.

Of course, if just update is needed, it can be done with a parameterized query:

<database cs="Data Source=.\SQLEXPRESS; Initial Catalog=XTest;Integrated Security=True;" >
	<rowset id="data" csvColumns="firstName,lastName,address">
		John, Smith, "90 Main Street, Lake Placid, NY"
	</rowset>

	<foreach rowsetId="data">
		<sql outTo="rc" mode="scalar">
			<param name="@first">${firstName}</param>
			<param name="@last">${lastName}</param>
			<param name="@addr">${address}</param>

			UPDATE Customers
			SET Address=@addr
			WHERE firstName=@first AND lastName=@last
			
			select @@rowcount
		</sql>
		<print>Rows updated=${rc|'none'}</print>
	</foreach>

	<sql outTo="^out" mode="query">Select * from Customers</sql>
</database>

Transactions

And the last but not least, transactions can be done too (using the usual TransactionScope from .NET2). Transaction commits automatically if the block exits normally, and rollbacks if leaving the block via exception

<database cs="Data Source=.\SQLEXPRESS; Initial Catalog=XTest;Integrated Security=True;" >
	<print>Before</print>
	<sql outTo="^out" mode="query">Select * from Customers</sql>
	<try>
		<transaction isolationLevel="serializable" timeout="00:05:00">
			<sql>
				UPDATE Customers
				SET Address='Test'
				WHERE lastName='Smith'
			</sql>
			<sleep timeout="00:00:05" />
			<throw>Oops!</throw>
		</transaction>
	</try>
	<finally>
		<print>After</print>
		<sql outTo="^out" mode="query">Select * from Customers</sql>
	</finally>
</database>

Executing programs

Being a batch file replacement language, XSharper of course can execute other executable files. And also in a more convenient way than traditional batch files, as output redirection and argument escaping (a major annoyance when writing Windows batch files) is handled w/o hacks and temporary files.

<shell>echo Hello world!</shell>

Output may be redirected to a variable or output stream

<shell >echo This goes to a real console, no redirection</shell>
<shell outTo="^out">echo This goes to a redirected output stream!</shell>
<shell outTo="^info">echo This goes redirected to info stream!</shell>

By default the output is expected to be text in default encoding (encoding can be changed using encoding attribute). It's also possible to redirect in binary mode, using binary="1" attribute.

There are different modes of command execution, specified via mode attribute:

comspec execute cmd /c program
batch save the shell command as a batch file, execute it, and then delete the batch file
direct the first argument is expected to be a name of an executable file
shellExecute use shellExecute, useful for direct opening of Word documents, or printing (specify verb='print') and so on
auto If verb is empty, use comspec otherwise shellexecute

Exit code may be saved to a variable via exitcodeTo attribute. By default, unless ignoreExitCode attribute is set, exception is thrown if the exit code is non-zero.

XSharper also provides assistance with argument escaping, by automatically escaping arguments with spaces inside with double quotes, and escaping " and other special characters with \ (which works fine with most applications but unfortunately not all as command line parsing in Windows is poorly standardized).

For example, the code below creates a temp batch file, and executes it with parameters, which are automatically escaped as needed. Parameters may be specified via <param> sub-element, which may also accept arrays, as demonstrated in the following example:

<set name="batch">
	@echo off
	echo args=[%*]
	set count=0
	:loop
		if "%~1"=="" goto end
		echo arg[%count%]=%1
		set /a count=%count%+1
		shift
		goto loop
	:end
</set>

<shell mode='batch' value="${batch}">
	<param>No-spaces</param>
	<param>This param is with spaces</param>
	<param>This is also with spaces</param>
</shell>
<print />

<!-- Create array of 3 strings -->
<set arr="${=new {'param1', 'param 2', 'param 333' }}"/>

<shell mode='batch'>
	${batch}
	<param>Non-array</param>
	<param switch="/xx">${arr}</param>
</shell>

The script outputs

args=[No-spaces "This param is with spaces" "This is also with spaces"]
arg[0]=No-spaces
arg[1]="This param is with spaces"
arg[2]="This is also with spaces"

args=[Non-array /xx param1 "param 2" "param 333"]
arg[0]=Non-array
arg[1]=/xx
arg[2]=param1
arg[3]="param 2"
arg[4]="param 333"

File operations

XSharper has built-in support for copy/move/delete/dir commands, which are useful for its batch file replacement functions. Support for .ZIP archives added in a similar fashion via #zipLib library.

The idea was to make group operations easy to use for simple cases, yet providing a capability to independently handle every individual file, confirm file overwrites and so on.

This is of the more complex and messier part of XSharper so a bit more explanation is due.

Algorithm

Implementation of the above functionality has a lot in common (and actually a common base class, ActionWithFilters) in its implementation.

There is a scanner of a directory tree, which filters files and directories matching a filter, and for every matching file contents of the block is executed. Hidden/System files are ignored unless hidden="1" is set.

<action filter="..." directoryFilter="..." hidden="0/1">
	user code executed FOR EVERY FILE 
	This is run BEFORE the copy/move/delete/etc. operation
	<try>
		This is also user code, but its exceptions will be caught
		This code may set skip variable to 1, if the operation should not be performed.
		...
		This is after the last user statement in try.
		If skip=0, here the actual Copy/Move/Delete etc will be executed with the provided file 
	</try>
	<catch>
		Executed if the operation fails for a particular file.

		Note that this catch block may be executed if an error occured
		in preparation for the action, even before user code is executed.
	</catch>
	<finally>
			Executed after the operation, whether it fails or succeedes
	</finally>
</action>

As this is technically a loop, action can break out of it.

What is special about the try/catch/finally block here is that <try> may be skipped, but <catch> will still catch copying exceptions.

A FileSystemInfo of the source file is passed to the block as "" and "from" variable (a prefix may may be added to variable via name attribute), which may be accessed as ${} or just $ inside XSharper expressions. If there is a destination location, it is set in "to" variable.

User-provided block decides whether to perform the command-specific operation (copy / move / delete / add to archive / extract) on the file by setting "skip" variable to true or false. If skip is false after completing the try block, the operation is executed.

Filter notation

There are two filters used for scanning directory tree, where both are optional. One filter is applied on found files, the other on found directories.

filter applies to found filenames without path. For example, for C:\Data\xxx.txt only xxx.txt will be evaluated against filter
directoryFilter applies to found directory names with full path. For example, for C:\Data\MyDir the whole string will be evaluated against filter.

Two different syntaxes may be used in the filter value:

wildcard A semicolon-separated list of masks. * = any number of any characters, ?=any single character. Mask may be prefixed with - to exclude, or optional + to include.

For example *.x??;-*.xls means all files with 3 letter extension that starts with x, except xls

pattern Normal regular expression
auto If filter starts with ^ it is considered to be pattern, otherwise wildcard.

List directory example

While I'm thinking of a better way to explain the above logic, here is an example that lists all *.XSH files in the current directory and its subdirectories, skipping ".svn" subdirectories. For every file its length is displayed:

<dir from="." filter="*.xsh" directoryFilter="-*\.svn" recursive="true">
	<print> ${=$.FullName}, Size=${=$.Length}</print>
</dir>

The following lists all files AND directories

<dir from="." sort="n" recursive="1" options="directories files">
	<print>${=$.fullname}</print>
	<noMatch>
		<print>Nothing found</print>
	</noMatch>
</dir>

sorted by name (sort="N"). Sorting order may be changed as in CMD.EXE, by specifying one or more of the letters:

N By name (alphabetic)
S By size (smallest first)
E By extension (alphabetic)
D By date/time (oldest first)
G Group directories first
A Access time
C Creation time
W Modification time (same as D)
- Prefix to reverse order

Please note that sort only applies to one directory, not to the whole tree, when listing directories recursively. This is consistent with dir behaviour in CMD.EXE

Copy or move files

Copy all files from current directory to r:\backup (as you see both try and catch may be skipped, to execute a piece of action after file copying completed)

<copy from="." to="r:\backup" recursive="true" hidden="true">
	<print>Copying ${from}=>${to}</print>
	<finally>
		<print>Done copying ${from}</print>
	</finally>
</copy>

Copy all files from current directory to r:\backup, ignoring all files with length>500

<copy from="." to="r:\backup" recursive="true" hidden="true">
	<set skip="${=$.length>500}" />
	<finally>
		<eval>
			$skip?null:c.Print($)
		</eval>
	</finally>
</copy>

Also support for overwriting existing file is available. In overwrite attribute it can be chosen whether to overwrite existing files always, only if newer, never, or ask user.

The piece below demonstrates copying procedure, confirming overwrite of the existing files:

<copy from="." to="r:\backup" recursive="true" hidden="true" overwrite="confirm">
    <!-- Skip will be set to true if destination file already exists -->
	<if condition="${skip}">
		<print newline='false'>File '${to}' already exists. Overwrite (Y/N)? </print>
		<while>
			<set key="${=char.ToLower(Console.Read())}" />
			<if condition="${=$key=='y'}">
				<set skip="false" />
				<break />
			</if>
			<if condition="${=$key=='n'}">
				<break />				
			</if>
		</while>
	</if>

	<try>
		<if isNotTrue="${skip}">
			<print newLine="false">Copying ${from} => ${to} .... </print>
			<sleep timeout="500" />
		</if>
	</try>
	
	<catch>
		<print outTo="^error">Ignoring ${=c.CurrentException.Message} when dealing with ${from}</print>
	</catch>
	<finally>
		<eval>
			$skip?null:c.writeline('done');
		</eval>
	</finally>
</copy>

Deleting files

This one just follows the pattern. There are two additional attributes. deleteReadOnly and deleteRoot, which control whether readonly files, and the root directory specified in from attribute, will be deleted.

Deleting a non-existing file or directory is not an error.

For example, the below deletes r:\backup.old, including hidden, system and read only files, printing the names of deleted files.

<delete from="r:\backup.old" 
			deleteRoot="false" deleteReadonly="true" hidden="true" recursive="true">
	<print>Deleting ${from}</print>
</delete>

Creating and unpacking ZIP archives

Again, same pattern. To ZIP r:\backup folder to a zip with default compression and password 'password' run

<zip from="r:\backup" to="x.zip" recursive="true" hidden="true" password='password'>
	<print>Archiving ${}</print>
</zip>

There are two inconvenient moments with an ancient format of ZIP, which should be kept in mind:

Unzipping the archive previously created is easy too:

<delete from="r:\tmp" recursive="true" hidden="true" />
<unzip from="x.zip " to="r:\tmp" hidden="true" password='password'>
	<print>Extracting ${from} => ${to}</print>

	<!-- Ignore errors -->
	<catch />
</unzip>

Downloads

Another common need in scripting is to download a file from somewhere, and the file can be a binary or text, and it can be just saved to a HDD, or processed first (ideally w/o any temporary files).

There are many ways to do it XSharper.

A dedicated download command, saving to a file.

<download from="http://www.xsharper.com/xsharper.exe" to="x.exe" />

Via a dedicated download command, saving to a variable as text:

<download from="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" to="x" />
<print>Length of downloaded string is ${=$x.length} characters</print>

ftp and http are supported, including their ftps:// and https:// variations. There is also a special scheme ftpa:// meaning "active ftp", as opposed to default passive ftp.

While download action is useful, it may be sometimes more to use URLs for remote files in the same way as local filenames are used.

<!-- count how many times word appendTo is mentioned in jquery -->
<set url="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" />
<readtext from="${url}" outTo="jq" />
<set cnt="${0}" />
<regex pattern="\bappendTo\b" value="${jq}">
	<set cnt ="${= $cnt +1 }" />
</regex>
<print>Count (interpreted) =${cnt}</print>

<!-- all of the above in a single C# line -->
<?_ 
	c.WriteLine("Count (C#) = {0}", 
		new Regex("\\bappendTo\\b").Matches(c.ReadText(c.GetStr("url"))).Count); ?>

<!-- all of the above in a single interpreted line -->
<print>Count (XSharper) = ${=new Regex('\bappendTo\b').Matches(c.ReadText(c["url"])).Count }</print>

Regular expressions

Dealing with regular expressions is a very essential part of many text parsing operations. XSharper regex action is a wrapper around Regex .NET class, and is a block that executes once for every match, setting captures as variables prefixed with name attribute (if empty, and capture name is numeric, underscore is added ):

<set name="text" >The quick brown fox jumps over the lazy dog</set>

<!-- prints [The] and  [the] as default option is IgnoreCase -->
<regex pattern="(the)" value="${text}">
	<print>[${_1}]</print>
</regex>

By default search uses IgnoreCase option. But options can be changed:

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<!-- prints only [the] -->
<regex pattern="(the)" value="${text}" options='none'>
	<print>[${_1}]</print>
</regex>

A piece of code may be executed if no matches were found:

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<!-- prints No cats in sight! -->
<regex pattern="(cat)" value="${text}" options='none'>
	<print>[${_1}]</print>
	<nomatch>
		<print>No cats in sight!</print>
	</nomatch>
</regex>

Captures

Captures are available inside the regex block as variables. For captures without name, _1 variable is set for first capture, _2 for second, etc. _0 variable is set to the found string.

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<regex pattern="brown (?'animal'\w+) (\w+)" value="${text}">
	<print>_0=[${_0}], _1=[${_1}], animal=[${animal}]</print>
</regex>
<print>After the block animal=${animal|'undefined'}</print>

Output is below, demonstrating that by default capture variables are not propagated beyond the regex body:

_0=[brown fox jumps]
_1=[jumps]
animal=[fox]
After the block animal=undefined

To get regex just to set capture variables, setCaptures may be set. Additional attributes define that the loop should exit after the first match is found, and that all capture variables should get 'x:' prefix:

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<regex pattern="brown (?'animal'\w+) (\w+)" value="${text}" setCaptures='true' count='1' name='x:'/>
<print>x:0=[${x:_0}], x:1=[${x:_1}], x:animal=[${x:animal}]</print>

Output is now

x:0=[brown fox jumps], x:1=[jumps], x:animal=[fox]

Replacement

replace attribute can be used to replace text

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<!-- prints The quick brown cat jumps over the lazy cat -->
<regex pattern="(fox|dog)" replace="cat" options='none' outTo="text1">${text}</regex>
<print>${text1}</print>

Replace with back-reference:

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<!-- prints The quick brown cat (used to be 1) jumps over the lazy cat (used to be 1) -->
<regex pattern="(fox|dog)" 
		replace="cat (used to be ${1})" options='none' outTo="text1">${text}</regex>
<print>${text1}</print>

Note that the result is unexpected, because ${1} is expanded to '1' before being passed to regular expression. Can deal with this issue using a temp variable:

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<!-- The quick brown cat (used to be fox) jumps over the lazy cat (used to be dog) -->
<set repl="cat (used to be ${1})" tr='none' />
<regex pattern="(fox|dog)" replace="${repl}" options='none' outTo="text1">${text}</regex>

or by using a different escape sequence:

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<!-- The quick brown cat (used to be fox) jumps over the lazy cat (used to be dog) -->
<regex pattern="(fox|dog)" replace="cat (used to be ${1})" options='none' outTo="text1" tr='expandDual'>${{text}}</regex>

or by using an internal expression:

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<!-- The quick brown cat (used to be fox) jumps over the lazy cat (used to be dog) -->
<regex pattern="(fox|dog)" replace="${='cat (used to be ${1})'}" options='none' outTo="text1" tr='expandDual'>${{text}}</regex>

or by just using C#-like syntax:

<set name="text" >The quick brown fox jumps over the lazy dog</set>
<print>${=Regex.Replace($text,'(dog|fox)','cat (used to be ${1})')}</print>

Miscellaneous

Controlling NT services

A small convenience feature, to start/stop NT services.

<!-- Restart W3SVC -->
<service serviceName="w3svc" command='isInstalled' outTo='installed' />
<print>Service is ${=$installed?'installed':'not installed'}</print>
<if isTrue='${installed}'>
	<service serviceName="w3svc" command='stop' />
	<print>Stopping</print>
	<service serviceName="w3svc" command='waitForStopped' />
	<print>Stopped</print>
	<service serviceName="w3svc" command='start' />
	<print>Starting</print>
	<service serviceName="w3svc" command='waitForRunning' />
	<print>Done</print>
</if>

Delaying and timing script execution

<!-- Wait for 5 seconds, then for 3 more seconds, then print the elapsed time (should be around 8 seconds if my math is right) -->
<timer format="elapsed" outTo="tm">
	<print newline='false'>Waiting for 5 seconds...   </print>
	<sleep timeout='00:00:05' />
	<print newline='false'>Almost ...</print>
	<sleep timeout='3000' />
	<print>Done!</print>
</timer>
<print>Elapsed: ${tm}</print>