VanDyke Software Forums

VanDyke Software Forums (
-   Scripting (
-   -   ReadString() Timeout (

ngrison 06-11-2018 09:30 AM

ReadString() Timeout

I am trying to capture the output of a system. Sometimes this output can be long, sometimes short, and there is no final character or character string that I can reliably use to identify the end of transmission. What I would like to do is capture everything until nothing is received for a certain amount of time.

The ReadString() function, which returns character by character, would be ok, but the problem is that it doesn't seem possible to include a timeout value. It's only possible to include a timeout value if looking for a specific string(s).

Do you know if there is any way to timeout ReadString()? Thank you.

ekoranyi 06-11-2018 10:00 AM

Hi ngrison,

I don't know that ReadString will fit your needs.


If a timeout parameter is provided, and ReadString reaches the timeout period without receiving the specified string(s) from the remote, ReadString will return an empty string.
The timeout is used when looking for a specific string, not to determine when new data stops appearing on screen.

Can you tell me a little more about the problem you are running into? We have often found that using the prompt as a unique string is a useful option. Is there something preventing waiting for your prompt to appear to signal a command finishing?

Are you planning to manipulate or use the data you capture in the script or would creating log file of the data be an option?

With a better idea of what you are trying to accomplish and the restrictions in place I may be able to help propose and alternative solution.

ngrison 06-12-2018 02:48 AM

Hi Eric,

My main issue is that I am working with some devices that have very long prompts. So long that if the window is standard size it will be displayed over two lines, with usually just a few characters on the second line. In the worse case we'd have say, just the final # symbol on the second line.

All methods to ge prompt only get what's on the current line of the cursor. With this, we'd get something short. If we get just a few characters, it's a very unreliable method to execute a command and wait for it to appear as there is a chance that it will appear in the output itself.

What I'd therefore like to do is get the full prompt, whether it's on 1 or two lines or more. That way I can look for the whole prompt at the end of executed commands, which is much more reliable.

My idea was to read all incoming characters until output stops but as per your response it's likely not possible. I don't know if something can be done with "get". As we don't know the number of lines to expect it's tricky.

None-the-less I think a timeout for ReadString() would be a very good idea. It is available for good reason when using ReadString([data],10) because sometimes things don't go as we expect. The same is true if we read character by character.

ekoranyi 06-12-2018 08:49 AM


Are you planning to manipulate or use the data you capture in the script or would creating log file of the data be an option?

One option may be to enable logging, run your command, then disable logging when the screen contents stop changing. This may allow you to capture the needed information for later review, but would make using the data inside the script more difficult.

Does this seem like an option that would work for you?

ngrison 06-13-2018 03:48 AM

Thanks for the suggestion but the script does various things with logs so it can't be started/stopped.

After various tests, I ended up doing the following:

def GetMultiLinePrompt(tab):
bSynchBefore = tab.Screen.Synchronous
tab.Screen.Synchronous = True
tab.Screen.IgnoreEscape = True
linePrompt = GetLineLeftOfCursor(tab)
fullPrompt = tab.Screen.ReadString(linePrompt,5) + linePrompt
tab.Screen.Synchronous = bSynchBefore
return fullPrompt

WaitForScreenContentsToStopChanging is a modified version that doesn't switch Synchronous off.

It reads the prompt left of the cursor, and because synchro remains enabled it's then possible to do a ReadString all the way to that string. That gives us the full prompt (including the CRs) string.

When running commands I can then use WaitForString(fullPrompt) in a more reliable way. It seems to be working fine so far.

I have noticed what looks like a bug though. Let's say that I have a device that has the following prompt:
my_very_very_very_long_prompt #

If the window is very narrow, it is correctly displayed in the SecureCRT window as:
mpt #

But if I capture the output with ReadString, the string is actually (notice the extra m):
mpt #

I've tried various times, and it turns out that the first character of the second line is always appended to the first line. It looks like a bug?

ekoranyi 06-13-2018 01:05 PM

Hi ngrison,

Unfortunately the information you provided is not conclusive. It appears you may have inadvertently introduced a bug in the code that can impact the data seen/received.

WaitForScreenContentsToStopChanging with Synchronous true will not provide the desired or expected behavior. With Synchronous set to the true the only time your screen gets updated is if you use a WaitFor or ReadString, and because your modified version of the function does not contain any WaitFor or ReadString, the contents of your screen will not ever get updated while in that function. With that being the case WaitForScreenContentsToStopChanging will always return after the 250 ms delay, even if your remote is still sending data. For this function to work as expected it will need Synchronous set to false.

With a bit of additional information I may be able to suggest alternative methods of determining if your commands are finished running. What type of devices are you connecting to?

ngrison 06-13-2018 05:08 PM

Thanks for the detailed explanation, it really helps to deeply understand how the function work. I use my scripts to connect to various devices such as F5 load balancers or ASA firewalls.

Thanks to your explanation I managed to come up with two different functions to achieve what I wanted to achieve. Both seem to work well and give me a string that is the full prompt, even if over multiple lines. One of them with Get2, getting the lineleftofcursor and then looking up the screen until we see this line again. The other uses the GetLineLeftOfCursor function but then simply issues another \r and catches the full prompt with a ReadString(). From my tests this evening both seem to work fine. Maybe I'll use the ReadString one, since the string generated will be used by the WaitForString function. It may reduce the risk that the prompt string wasn't correct.

Option 1:

def GetMultiLinePrompt_Get(tab):
bSynchBefore = tab.Screen.Synchronous
tab.Screen.Synchronous = False
tab.Screen.IgnoreEscape = True
ScreenContent = tab.Screen.Get2(
shortPrompt = ScreenContent[len(ScreenContent) - 2]
fullPrompt = [shortPrompt]
for x in ScreenContent[len(ScreenContent) - 3::-1]:
if x == shortPrompt:
fullPrompt.insert(0, x)
fullPrompt = "\n".join(fullPrompt)
tab.Screen.Synchronous = bSynchBefore
return fullPrompt

Option 2:

def GetMultiLinePrompt_Wait(tab):
bSynchBefore = tab.Screen.Synchronous
tab.Screen.Synchronous = False
tab.Screen.IgnoreEscape = True
shortPrompt = GetLineLeftOfCursor(tab)
tab.Screen.Synchronous = True
fullPrompt = tab.Screen.ReadString(shortPrompt,10) + shortPrompt
tab.Screen.Synchronous = bSynchBefore
return fullPrompt

ekoranyi 06-14-2018 09:01 AM


Another, and potentially "easier", option would be to temporarily increase the number of columns available. This would help prevent your prompt from wrapping saving some headache on trying to capture wrapped prompts.

' Set screen width to be at least 256 chars wide, so
 ' that we can reliably drive the remote without much
 ' risk of line remote-shell-driven line wrapping getting
 ' in the way:
 nOrigColumns = g_objScriptTempTab.Screen.Columns
 nOrigOnResize = g_objScriptTempTab.Session.Config.GetOption("Resize Mode")
 ' Turn on font scaling so that we can have a lot of columns, but
 ' the window won't grow so large (font will shrink, instead).
 If CINT(nOrigOnResize) <> 1 Then
    g_objScriptTempTab.Session.Config.SetOption "Resize Mode", 1
 End If
 g_objScriptTempTab.Session.Config.SetOption "Cols", 256

 ' This is where you do the work you need to do
 ' ##########################################################
 ' Then, once all the work has been done with the script, you
 ' set things back the way they were
 ' ----------------------------------------------------------
 g_objScriptTempTab.Session.Config.SetOption "Cols", nOrigColumns
 g_objScriptTempTab.Session.Config.SetOption "Resize Mode", CINT(nOrigOnResize)

By default the maximum number of rows available is 256. If you need more than 256 rows to ensure your prompt doesn't wrap that can be configured in SecureCRT outside of the script.

Does this seem like a viable option for you?

All times are GMT -6. The time now is 11:17 AM.