SecureCRT Scripting FAQ

SecureCRT® supports several scripting languages so you can automate tasks and processes. On the Windows platform, ActiveX scripting languages include VBScript, JScript, and Perlscript. Python is supported on Windows, macOS, and Linux. A script recorder helps you build your keystrokes into a VBScipt or Python script.

More information on scripting in SecureCRT can be found on the Windows Scripting Examples page, the Python Scripting Examples page, and the VanDyke Software online Scripting Essentials guide.

To send or embed control characters in strings in JScript/Javascript convert them to octal values with a prepended "\" character for example:

// send ^C, (decimal 3)
crt.screen.Send("\003")

// send ^D, (decimal 4)
crt.screen.Send("\004")

// send ^M, (decimal 13)
crt.screen.Send("\015")

In VBScript use the Chr() function with the decimal value of the character you want to send:

' Send ^C
crt.screen.Send Chr(3)

' Send ^D
crt.screen.Send Chr(4)

' Send ^M
crt.screen.Send Chr(13)

You can use the VBScript '&' operator to concatenate strings with the Chr() function, for example:

crt.screen.Send "hello" & Chr(10) & "goodbye"

You cannot run programs directly in an ActiveX script. However, if you have Microsoft's Windows Script Host (WSH) installed, you can tell WSH to run it for you.

The following VBScript statements use WSH to start Internet Explorer with a command-line argument:

Dim shell
Set shell = CreateObject("WScript.Shell")
shell.Run """C:\Program Files\Internet Explorer\iexplore.exe""https://www.vandyke.com"

Here's the same thing in JScript:

var shell = new ActiveXObject("WScript.Shell");
shell.Run("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\"https://www.vandyke.com");

Note that the Run() function uses whitespace to separate program names from possible command-line arguments. In order to deal with paths with spaces, it is necessary to add extra quotes in both languages to make it clear.

SecureCRT scripts require a header that identifies the script language and the interface version. The header should begin on the first line of the script file and each header line should contain the following two lines:

# $language = "language"
# $interface = "1.0"

Note that each header line must have a "#" character as the first character on the line. Header lines may also be empty lines that begin with "#" followed by whitespace and a carriage return.

You should substitute <language> with the name of the actual script language in which the script is written. For example, a VBScript starts with this:

# $language = "VBScript"
# $interface = "1.0"

VBScript code here...

A JScript script starts with this header:

# $language = "JScript"
# $interface = "1.0"

// JScript code here...

Yes. If you have installed the PerlScript engine, then you can run SecureCRT scripts written in Perl. You can download and find information regarding Perlscript from:

www.activestate.com

When using Perlscript, there are a couple of issues you should be aware of that will make debugging your scripts easier:

By default, runtime errors in your Perlscript won't be reported unless you enable warnings about these errors. Be sure you include these lines in your script if you want to be informed of errors:

# Perl doesn't warn about errors by default. Enable errors.
use Win32::OLE;
Win32::OLE->Option(Warn => 3);

Another problem you may find with Perlscript is that runtime errors that occur within the "main" routine invoked by SecureCRT aren't reported back to SecureCRT in an informative manner. This is believed to be a bug that was present in the latest implementation of Perlscript (build 518). The following simple script demonstrates the problem:

# $language = "PerlScript"
# $interface = "1.0"

# Perl doesn't warn about errors by default. Enable errors.
use Win32::OLE;
Win32::OLE->Option(Warn => 3);

# display the version
$crt->Dialog->MessageBox($crt->{'Version'});

# 1. generate an error: invalid property $crt->Dialog->MessageBox($crt->{'XYZZY'});

sub main {

# display the version
$crt->Dialog->MessageBox($crt->{'Version'});

# 2. generate an error: invalid property
$crt->Dialog->MessageBox($crt->{'XYZZY'});

}

This script gets a runtime error when it tries to fetch an invalid property both at the global level and within the "main" routine. The first runtime error should display some verbose information indicating the line and the nature of the problem, however the second runtime error will simply be reported as "main failed..." with no indication why. SecureCRT will invoke your main subroutine if it exists, but you are not required to have one. In order to work around this problem, you may want to develop your Perlscript code outside of a main subroutine.

Part of what is going on is VBScript distinguishing between different kinds of procedures (Sub or Function). Whether there is a return value being part of that distinction. The upshot is this: You will need parentheses if you are calling a function that returns a value and you are using the return value, or if you are using the "Call" keyword. For example:

' Parentheses can't be used here. Disregarded return value
' makes this a subroutine call.
crt.screen.WaitForString "string", 5

' Parentheses needed here. Return value makes this a
' function call.
Dim result
result = crt.screen.WaitForString("string", 5)

' Parentheses are always needed when using 'Call'
Call crt.screen.WaitForString("string")
Call crt.screen.WaitForString("string", 5)

' With one argument both of these forms are legal.
crt.screen.WaitForString "string"
crt.screen.WaitForString("string")

SecureCRT® does not offer direct support for reading and writing to files, but the ActiveX scripting language you are using probably offers a mechanism to access files.

Microsoft's VBScript and JScript languages both allow you to create instances of "filesystem objects" that offer a flexible interface for manipulating files.

The following VBScript sample code creates a filesystem object, opens a file, then reads the file line by line and sends it to a server:

# $language = "VBScript"
# $interface = "1.0"

Sub main

' Open a file, read it in & send it one line at a time
Dim fso, f, str
Const ForReading = 1

Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile("c:\temp\file.txt", ForReading, 0)

Do While f.AtEndOfStream <> True
     str = f.Readline
     crt.Screen.Send str & Chr(13)
     ' Wait for my prompt before sending another line
     crt.Screen.WaitForString("linux$")
Loop

End Sub

The following JScript sample code performs a similar operation:

# $language = "JScript"
# $interface = "1.0"

function main()
{
        var fso, f;
        var ForReading = 1, ForWriting = 2;

         fso = new ActiveXObject("Scripting.FileSystemObject");
         f = fso.OpenTextFile("c:\\temp\\file.txt", ForReading);

         while ( f.AtEndOfStream != true )
         {
             var str = f.Readline();

             // Append a CR to the line (decimal: 13, octal: 15).
             str += "\015";

             crt.Screen.Send( str );
         }
};

You can pass arguments to ActiveX scripts (running in SecureCRT) by using the /ARG command-line option to pass arguments to SecureCRT and then retrieving those arguments in your script using the "Arguments" object.

The example below is a common script that connects to multiple hosts and performs the same operation on each host, having been passed variable arguments for hostname, username, and port.

' Detect lack of proper arguments.
If crt.Arguments.Count < > 3 Then
MsgBox "This script requires hostname, username, and port arguments"
Exit Sub
End If


hostname = crt.Arguments(0)
username = crt.Arguments(1)
port = crt.Arguments(2)

The arguments are then made available to scripts by starting different instances of SecureCRT and using the /ARG command-line option to pass the data. In the example below, two instances of SecureCRT are started and different hostnames, usernames, and ports are made available to scripts being run by those instances of SecureCRT:

SecureCRT.exe /ARG router1 /ARG admin1 /ARG 5001
SecureCRT.exe /ARG router2 /ARG admin2 /ARG 2400

First, if you have not yet read through the Scripting Essentials Guide, we strongly urge you to do so and become familiar with the concepts — at least Chapter 4, as it provides specific guidance on how to ensure that you keep things "in sync". The Scripting Essentials Guide will likely answer many of your questions.

The purpose of this FAQ is to highlight common pitfalls that a number of first-time (and even long-time) script writers encounter. Following each pitfall highlighted is a corresponding "Best Practice" section.

Pitfall: Sending commands before the remote is ready to receive anything.

One mistake many SecureCRT script writers make is failing to ensure that the remote system is ready to receive commands before actually sending any commands.

The crt.Screen.Synchronous property being set to True is essential for keeping things in sync. Be aware that when crt.Screen.Synchronous is set to True and while the script is running:

  • EVERYTHING received from the remote system will be queued up in a pre-display buffer.
  • NOTHING in this pre-display buffer will be displayed to the screen until a call to WaitFor*() or ReadString() is made or until the script terminates (completes or is canceled).
    • When a WaitFor*() or ReadString() call is made, text within the not-yet-displayed buffer that does NOT match what you told SecureCRT to wait-for/read is transferred out of the not-yet-displayed buffer and sent to the screen (where it appears for display).
    • Once text has been displayed to the screen, WaitFor*() and ReadString() cannot act upon that text because it has already been received and displayed. If you need to grab data that is already on the screen, use crt.Screen.Get() or crt.Screen.Get2(). The caveat here is that you have to know the coordinates (row_start, col_start, row_end, col_end) of the text as it appears on the screen. This is why most scripting solutions will involve WaitForString(), WaitForStrings(), or ReadString() to know when the remote system is ready to receive commands/data.
  • If you call Send() before first waiting to make sure the remote system is ready to receive commands, if you then call WaitFor*() or ReadString() some time after your first Send(), it won't be the output of any of your Send() command that will be first in line to be found by your the initial WaitFor*() or ReadString() call. Instead, the data that has been queued up for display since the beginning of your connection will be searched; and if you haven't first made sure that the remote system was ready to receive commands (by waiting for your shell prompt initially before sending anything to the remote host) your initial WaitFor*() or ReadString() call is going to return positives on matching data that was already received (and queued for display) before you sent any commands at all.

Best Practice

In any script that performs a connection (or is a logon script itself), always call Screen.WaitForString("<your-shell-prompt-text-here>") near the beginning of your script to (a) make sure the host is ready to receive commands, and (b) to consume any output that may be buffered (not yet displayed to the screen) so that it doesn't interfere with any WaitFor*() or ReadString() calls following your Send().


Pitfall: Calling Send() multiple times in succession without intervening WaitForString() calls after each Send().

Another pitfall script writers encounter is the mistake of performing two or more Send() operations in succession (where they are pressing Enter as part of each Send()) but then fail to wait for the shell prompt to appear after each first Send() before the next Send() is performed.

In the case of this mistake, a WaitForString() call made after successive Send() calls (again, where the Send() involves pressing Enter) will end up finding the shell prompt that appeared as a result of the first Send().

Best Practice

For each Send() where the text you're sending involves carriage return ("\r" in Python, or vbcrlf — same as chr(13) — for VBScript), make sure you have a matching WaitForString() call.


Pitfall: Waiting for a simplistic single-character shell prompt when the output of a command might include that character.

This pitfall involves the script writer being unaware of the ramifications of the output of commands potentially including the very character they are waiting for as an indication of the command completing.

To be certain, a script writer should be waiting for the full CLI shell prompt (as long as the full shell prompt isn't just a ">" or a "#") because it's less likely that the output of any command would include that as part of its data. If all you are doing is waiting for a "#" and the output of your command has one of those "#" characters, then the subsequent WaitForString("#") call is going to return after finding the "#" in the command's output, rather than the "#" that is part of the CLI shell prompt.

Best Practice

Wait for the entire shell prompt to appear so that there aren't any false positives triggered by the output of the command you're running.


Pitfall: Output doesn't appear on the screen in real time (or until after the script has completed).

Individuals will sometimes report something similar to the following:

While a script is running, I observed that the text received from the connected host isn't displayed in real time. Only when the script execution is complete (or when the script is cancelled), do I see the commands/output printed to the screen. Is there any way to make it real-time?

In such cases where your script is looping and only performing Screen.Send() operations after a delay, if you don't see output appear in SecureCRT's terminal window until after the script has terminated, it means that your script has set Screen.Synchronous = True, but you're never calling any Screen.WaitFor*() or Screen.ReadString() methods.

As mentioned earlier in this post,

…when the crt.Screen.Synchronous property is set to True, while the script is running:

  • EVERYTHING received from the remote system will be queued up in a pre-display buffer;
  • NOTHING in this pre-display buffer will be displayed to the screen until a call to WaitFor*() or ReadString() is made or until the script terminates (completes or is canceled).
  • When a WaitFor*() or ReadString() call is made, text within the not-yet-displayed buffer that does NOT match what you told SecureCRT to wait-for/read is transferred out of the not-yet-displayed buffer and sent to the screen (where it appears for display).

Best Practice

If all your script does is send a command every 5 seconds, and you're never using Screen.WaitFor*() or Screen.ReadString(), then make sure that Screen.Synchronous = False in your code; otherwise, output received from the connected host will be queued up for display and will only appear after your script has terminated/canceled.

Alternatively, if you want to keep Screen.Synchronous = True (because at some point you might care about waiting for specific text to appear later), then insert a Screen.WaitForString() call in your loop, passing in a string of text that you'll never expect to find, with a timeout value equal to the number of seconds you wish to pause in your loop, instead of using crt.Sleep() to do your pauses. For example:

# $language = "Python3"
# $interface = "1.0"

crt.Screen.Synchronous = True
# Send the same command over and over until the script is canceled;
while True:
    crt.Screen.Send("show ip interface brief\r")

    # Wait for 5 seconds before looping. Use WaitForString()
    # so that non-matching text will appear on the screen as
    # soon as it has been received:
    crt.Screen.WaitForString("...Never expecting to find this text...", 5)

Is there another approach to keeping things "in sync"?

Another way to sync things up is to take the approach of:

  1. Send a command (but don't press Enter yet).
  2. Wait for the text of the command itself to appear.
  3. Then press Enter to actually run the command.
  4. Now you can Wait...() for the shell prompt, knowing that the shell prompt (if not part of the command's output itself) will signal the command's completion.

For example:

strShellPrompt = "MyHost'sShellPromptText# "

' 1. Send the command (but don't "press" {Enter}!):
strCommand = "ls -alR /"
crt.Screen.Send strCommand

' 2. Wait for the text of the command to appear:
crt.Screen.WaitForString strCommand

' 3. Now "press" {Enter} to run the command
crt.Screen.Send vbcr 'same as chr(13), but easier to type

' 4. Now wait for the shell prompt to appear:
crt.Screen.WaitForString strShellPrompt

Three Fast Ways to Learn More…

  1. Read or download one of our secure solutions white papers.
  2. Download a free evaluation copy of our products.
  3. Let us help define the right Secure Shell solution for your company.

VanDyke Software uses cookies to give you the best online experience. Before continuing to use this site, please confirm that you agree to our use of cookies. Please see our Cookie Usage for details.