More .NET components

Scripting

Scripting object #

Scripting object is the core of Rebex Terminal Emulation's powerful scripting API. It's available in both TerminalControl and VirtualTerminal:

CSharp

// get the scripting object from
// a TerminalControl or VirtualTerminal
Scripting scripting = terminal.Scripting;

// do some scripting
// ...

VisualBasic

' get the scripting object from
' a TerminalControl or VirtualTerminal
Dim scripting As Scripting = terminal.Scripting

' do some scripting
' ...

Alternatively, you can get a stand-alone Scripting object by calling Ssh.StartScripting method:

CSharp

// create an instance of Ssh object
var ssh = new Rebex.Net.Ssh();

// connect and log in
ssh.Connect(serverName);
ssh.Login(username, password);

// start a scripting session
// and get a stand-alone scripting object
Scripting scripting = ssh.StartScripting();

// do some scripting
// ...

VisualBasic

' create an instance of Ssh object
Dim ssh = New Rebex.Net.Ssh()

' connect and log in
ssh.Connect(serverName)
ssh.Login(username, password)

' start a scripting session
' and get a stand-alone scripting object
Dim scripting As Scripting = ssh.StartScripting()

' do some scripting
' ...

Tip: You can get an ITerminal used by the stand-alone Scripting object through its Terminal property.

Automatic prompt detection #

In most cases, the first thing you need to do is to make sure the remote system's command prompt can be reliably detected.

DetectPrompt method detects the prompt automatically in most cases:

CSharp

// detect the prompt automatically
scripting.DetectPrompt();

VisualBasic

' detect the prompt automatically
scripting.DetectPrompt()

Note: This method should only be called when the remote system is actually showing the prompt and is waiting for input. Calling it while a command has been executed will usually result in a failure (unless the command has a command prompt of its own).

Custom prompts #

If automatic prompt detection can't be used, you can specify the remote system's command prompt yourself using Prompt property:

CSharp

// specify the remote prompt
scripting.Prompt = "> ";

VisualBasic

' specify the remote prompt
scripting.Prompt = "> "

The prompt can be specified by a simple text, by text with wildcards or by a regular expression:

  1. Simple text - Matches an exact phrase.
    If the remote server prompt is the server hostname ending with '#' and a whitespace, the "hostname# " string can be used to match the prompt.
    Note that this type of prompt requires that server prompt to remain constant during the whole session. In practice, prompts of many Unix-based shells contain the current path, making it necessary to use one of the other prompt specification methods.
  2. Wildcards - Matches a phrase with wildcards.
    If the server prompt can vary slightly, it is possible to utilize wildcards to handle prompt changes or variants. This type has to be prefixed with the "mask:" string when being set. Available wildcards:
    '?' - matches any one character.
    '*' - matches any character sequence including zero length sequence.
    Let's say the server's prompt is "username@hostname:currentPath$ " (as in most Unix shells), where currentPath changes during the session and the '$' character can change to '#' if logged as the "root" superuser (administrator).
    In this case, "mask:userName@serverName:*? " string can be used to match the prompt.
  3. Regular expression - Matches a regular expression.
    This is the most versatile prompt expression type. It uses regular expression to match command response endings. This type has to be prefixed with the "regex:" string when being set. Let's say the server's prompt is "username@hostname:currentPath$ " (the same as above), where currentPath changes during the session and the '$' character can change to '#' if logged as the "root" superuser (administrator).
    In this case, "regex:(userName[@]serverName[:].*[$])|(serverName[:].*[#]) " string can be used to match the prompt.
    Regular expressions are not the topic of this tutorial, but lots of information about them can be found elsewhere.

Executing commands #

Once you have detected or specified the remote system's command prompt, you can easily execute a command by calling the SendCommand method and read its response with ReadUntilPrompt method:

CSharp

// start a command
scripting.SendCommand("ls -lR");

// and read its response
string response = scripting.ReadUntilPrompt();

VisualBasic

' start a command
scripting.SendCommand("ls -lR")

' and read its response
Dim response As String = scripting.ReadUntilPrompt()

Tip: If you only need to execute one simple command and read its response, use Ssh.RunCommand.

Simulating keyboard keys #

There are several Send method overloads that can be used to simulate keyboard input. From the remote terminal's point-of-view, these are indistinguishable from the actual key presses.

CSharp

// simulate pressing an 'Escape' key
scripting.Send(FunctionKey.Escape);

// simulate pressing 'Ctrl+C'
scripting.Send(ConsoleKey.C, ConsoleModifiers.Control);

// send capital 'X'
var key = new ConsoleKeyInfo('X', ConsoleKey.X, true, false, false);
scripting.Send(key);

// another way to send capital 'X'
scripting.Send("X");

VisualBasic

' simulate pressing an 'Escape' key
scripting.Send(FunctionKey.Escape)

' simulate pressing 'Ctrl+C'
scripting.Send(ConsoleKey.C, ConsoleModifiers.Control)

' send capital 'X'
Dim key = New ConsoleKeyInfo("X"c, ConsoleKey.X, True, False, False)
scripting.Send(key)

' another way to send capital 'X'
scripting.Send("X")

Sending data #

Use SendCommand method to send a command or other similarly-behaving text to the server (this includes usernames, passwords, etc.). In addition to sending the specified text, it also sends a proper end-of-line sequence and receives data from the server until an end-of-line is reached.

CSharp

// send a command (with enter key, etc.)
scripting.SendCommand("some text");

VisualBasic

' send a command (with enter key, etc.)
scripting.SendCommand("some text")

However, Scripting also provides several variants of Send method that are more low-level. They only send the specified text or simulate the specified key and don't perform any other processing:

CSharp

// send raw text (and do nothing else)
scripting.Send("raw text");

// send an 'Enter' key
scripting.Send(FunctionKey.Enter);

// send a byte array
scripting.SendData(byteArray, 0, byteArray.Length);

VisualBasic

' send raw text (and do nothing else)
scripting.Send("raw text")

' send an 'Enter' key
scripting.Send(FunctionKey.Enter)

' send a byte array
scripting.SendData(byteArray, 0, byteArray.Length)

Note: The data-sending methods don't actually require that the Prompt has been set prior to calling them. Also, when scripting TerminalControl, they don't require the processing mode to be changed to non-automatic.

Reading response #

There are several methods for receiving response from the server. ReadChar and ReadLine are straightforward and don't even require setting Prompt before calling them:

CSharp

// receive a single character
char c = scripting.ReadChar();

// receive a single line
string line = scripting.ReadLine();

VisualBasic

' receive a single character
Dim c As Char = scripting.ReadChar()

' receive a single line
Dim line As String = scripting.ReadLine()

However, when scripting multiple commands over a single shell, ReadUntilPrompt is usually more useful. As the name suggests, the Prompt has to be detected or set before calling it for the first time:

CSharp

// detect the prompt automatically
scripting.DetectPrompt();

// execute a command
scripting.SendCommand("echo Hello!");

// receive response until the remote system's
// prompt is encountered
string response = scripting.ReadUntilPrompt();

// execute another command
scripting.SendCommand("hostname");

// receive response for the second command
response = scripting.ReadUntilPrompt();

VisualBasic

' detect the prompt automatically
scripting.DetectPrompt()

' execute a command
scripting.SendCommand("echo Hello!")

' receive response until the remote system's
' prompt is encountered
Dim response As String = scripting.ReadUntilPrompt()

' execute another command
scripting.SendCommand("hostname")

' receive response for the second command
response = scripting.ReadUntilPrompt()

If the executed command is more advanced and might result in multiple outcomes, try ReadUntil method instead.

Tip: If you don't need to 'read' any response and just want the terminal to receive and process whatever data has been sent (and update the contents of the terminal screen), use Scripting.Process method.

Advanced response matching #

ReadUntil method receives server response until the specified condition is met. It accepts a wide range of different 'events' and even makes it possible to combine them. For example, it's easily possible to receive response until one of several events occur:

CSharp

// receive response until either the remote system's
// prompt or the 'Name: ' text is encountered
string response = scripting.ReadUntil(ScriptEvent.Prompt, ScriptEvent.FromString("Name: "));

VisualBasic

' receive response until either the remote system's
' prompt or the 'Name: ' text is encountered
Dim response As String = scripting.ReadUntil(ScriptEvent.Prompt, ScriptEvent.FromString("Name: "))

WaitFor behaves just like ReadUntil, but it returns an instance of ScriptMatch instead of the received response. This makes it possible to easily determine which of the specified conditions was actually matched:

CSharp

// receive response until either the remote system's
// prompt or the 'Name: ' text is encountered
ScriptMatch match = scripting.WaitFor(ScriptEvent.Prompt, ScriptEvent.FromString("Name: "));

// get the response received during the previous method if needed
string response = scripting.ReceivedData;

// determine what to do depending on whether the prompt was matched
if (match.IsPrompt)
{
    // do something
}
else
{
    // do something else
}

VisualBasic

' receive response until either the remote system's
' prompt or the 'Name: ' text is encountered
Dim match As ScriptMatch = scripting.WaitFor(ScriptEvent.Prompt, ScriptEvent.FromString("Name: "))

' get the response received during the previous method if needed
Dim response As String = scripting.ReceivedData

' determine what to do depending on whether the prompt was matched
' do something
If match.IsPrompt Then
    ' do something else
Else
End If

ScriptEvent object is easy-to-use:

CSharp

// strings get converted to ScriptEvent.FromString(value)
match = scripting.WaitFor("Name: ");

// supports operators such as 'and' and 'or'
match = scripting.WaitFor(event1 & event2);

// wait until a non-control character is received or the connection is closed
match = scripting.WaitFor(ScriptEvent.AnyText | ScriptEvent.Closed);

VisualBasic

' strings get converted to ScriptEvent.FromString(value)
match = scripting.WaitFor("Name: ")

' supports operators such as 'and' and 'or'
match = scripting.WaitFor(event1 And event2)

' wait until a non-control character is received or the connection is closed
match = scripting.WaitFor(ScriptEvent.AnyText Or ScriptEvent.Closed)

Tip: Setting Scripting.AutoMatchPrompt to true will make every call to WaitFor or ReadUntil automatically include ScriptEvent.Prompt.

Tip: See the scripting code for telnet authentication.

Response matching with tags #

To determine which of the multiple ScriptEvent objects was matched in a WaitFor call, use one of the various ScriptMatch properties. If this is still not sufficient to distinguish one 'event' from another, use custom tags:

CSharp

// receive response until either the remote system
// asks for name or password
ScriptMatch match = scripting.WaitFor(
    ScriptEvent.Prompt,
    ScriptEvent.Closed,
    ScriptEvent.FromString("Name: ").SetTag("name"), // set custom tag
    ScriptEvent.FromString("Password: ").SetTag("password") // set custom tag
    );

if (match.IsClosed)
{
    // exit
}
else if (match.IsPrompt)
{
    // send another command
}
else
{
    // get match info for an event with tag of "name"
    ScriptMatchInfo info = match.GetMatchInfoByTag("name");
    if (info != null)
    {
        // send the name
    }
    else
    {
        // get match info for an event with tag of "password"
        info = match.GetMatchInfoByTag("password");
        if (info != null)
        {
            // send the password
        }
        else
        {
            // fail
        }
    }
}

VisualBasic

' receive response until either the remote system
' asks for name or password
Dim match As ScriptMatch = scripting.WaitFor(
    ScriptEvent.Prompt,
    ScriptEvent.Closed,
    ScriptEvent.FromString("Name: ").SetTag("name"),
    ScriptEvent.FromString("Password: ").SetTag("password"))

If match.IsClosed Then
    ' exit
ElseIf match.IsPrompt Then
    ' send another command
Else
    ' get match info for an event with tag of "name"
    Dim info As ScriptMatchInfo = match.GetMatchInfoByTag("name")

    If info IsNot Nothing Then
        ' send the name
    Else
        ' get match info for an event with tag of "password"
        info = match.GetMatchInfoByTag("password")

        If info IsNot Nothing Then
            ' send the password
        Else
            ' fail
        End If
    End If
End If

Many supported events #

WaitFor, ReadUntil and CheckFor methods accept one or more ScriptEvent objects. The following table lists all the supported event types:

Event Description
And (event1, event2) Both of the conditions must be met.
AnyText Matches any character that is not part of an escape sequence (e.g. color change is not matched).
Closed Matches closed communication channel.
CursorAtPosition (row, column) Matches a cursor appearing at the specified position.
CursorInArea (row, column, width, height) Matches a cursor appearing in the specified area.
Delay (milliseconds) Matches a delay (no data arrived for the specified time span).
Duration (miliseconds) Matches time spent on receiving and processing data.
FromRegex (pattern) Matches the regular expression.
FromString (text) Matches the specified string.
Line Matches an end-of-line ('\n') sequence.
Not (event1) A condition must not be met.
Or (event1, event2) At least one of the conditions must be met.
Prompt Matches the prompt specified by Prompt property.
TextAtPosition (text, row, column) Matches the specified string appearing at the specified position.
TextInRegion (text, row, column, width, height) Matches the specified string appearing inside the specified region.
TextOnCursorLine (text) Matches the specified string appearing at the cursor line.
TextOnScreen (text) Matches the specified string appearing anywhere on the screen
Timeout Matches timed-out operation.

Remote exec #

When using SSH, it's possible to start scripting by launching a remote command instead of a shell. When the command ends, the scripting session ends as well. This is very useful if you need to ensure that the remote command won't suddenly fail and get back to shell without notice.

CSharp

// create an instance of Ssh object
var ssh = new Rebex.Net.Ssh();

// connect and log in
ssh.Connect(serverName);
ssh.Login(username, password);

// start command-line FTP client on a remote server
Scripting scripting = ssh.StartScripting("ftp test.rebex.net");

// do some FTP client scripting
// ...

VisualBasic

' create an instance of Ssh object
Dim ssh = New Rebex.Net.Ssh()

' connect and log in
ssh.Connect(serverName)
ssh.Login(username, password)

' start command-line FTP client on a remote server
Dim scripting As Scripting = ssh.StartScripting("ftp test.rebex.net")

' do some FTP client scripting
' ...

Note: To launch a remote command instead of shell when binding a TerminalControl or VirtualTerminal object to Ssh, set terminal's Options.RemoteCommand to the desired remote command before binding.

Keep-alive packet #

Use Scripting.KeepAlive method to prevent your session from timing out when there is no user interaction for a long time. This method sends a single keep-alive packet (SSH_MSG_IGNORE for SSH and NOOP for Telnet) to the server. To prevent the session from timing out, call it every minute or so.

Note: When using SSH transport layer, consider calling SshSession.KeepAlive instead.