Expect not matching - tcl

I had an expect script that was working; it contained the following lines:
expect "\[Y\]\> "
send "n\r"
The OS I'm interacting with changed, I now have a mixed environment of boxes. Some now have "[Y]>", some have "[1]>" at the point I'm trying to deal with.
I tried changing the code to:
expect {
"\[Y\]\> " { send "n\r";exp_continue }
"\[1\]\> " { send "2\r";exp_continue }
}
however, running in debug I see:
"Choose the password option:
1. Mask passwords
2. Plain passphrases
[1]>
expect: does "s...ses\r\n[1]> " (spawn_id exp5) match glob pattern "[Y]> "? no
"[1]> "? no
expect: timed out"
I don't understand why the revised code is not working, for either "[Y]> " or "[1]> ", when "[Y]> " was being matched before the 'else' statement was introduced.

The problem is with escaping the square brackets which is special to Tcl.
# Using braces for pattern
expect {
{\[Y]>} {puts Y}
{\[1]>} { puts 1}
}
# or
# Using double quotes
expect {
"\\\[Y]>" {puts Y}
"\\\[1]>" { puts 1}
}
We don't have to escape the closing bracket ] and the symbol >

Related

TCL: get single variable to be interpreted as multiple arguments

How can I get a single string variable with spaces in it in TCL to be interpreted as multiple arguments? I can't change the proc definition.
Here is an example of what I mean:
set my_options ""
if { "$some_condition" == 1 } {
append my_options " -optionA"
}
if { "$some_other_condition" == 1 } {
append my_options " -optionB"
}
set my_options [string trim $my_options]
not_my_proc ${my_options} ;# my_options gets interpreted as a single arg here and causes a problem:
# Flag '-optionA -optionB' is not supported by this command.
This is where you use the argument expansion syntax:
not_my_proc {*}$my_options
# ..........^^^
Although I'd recommend using a list instead of a string:
if for some reason the my_options string is not a well-formed list, you'll see an error thrown
if any of the options takes a space, a list is the proper data structure:
set my_options [list]
lappend my_options {-option1}
lappend my_options {-option2 "with a parameter"}
not_my_proc {*}$my_options

Expect script, help handling multiple options in order, and only once

expect {
"Error: 1*#" {
send_user "\nSpecific Error\n"
}
"Error: *#" {
send_user "\nError Happened\n"
}
"*#" {
send_user "\nNo Error\n"
}
}
Basically what I'm looking to happen is
Check for text "Error: 1", anything between and then the "#" prompt.
If found, send first message to the user
If that's not found check for "Error:" anything at all, and then the "#" prompt.
If found, send an error message to the user
If none of that is met, check for just the "#" prompt
If found, send a message to the user.
I can't reliably get it to work in that order and ONLY do one of those.
Any tips?
You can simply check for the regular expression pattern .*# and upon matching you can look for the desired words.
expect -re ".*#"
set match $expect_out(0,string)
if {[string first "Error: 1" $match]!=-1} {
# scenario 1
} elseif {[string first "Error:" $match]!=-1} {
# scenario 2
} else {
# scenario 3
}
If you really want all-in-one regular expression to match, instead of checking it using if condition, then you can use the following pattern,
(Error:)?\s*(1)?.*#
You have to check whether expect_out(1,string) and expect_out(2,string) exists or not. This is to ensure it contains the desired word.

In an expect script, how do I remove a set of special characters from a string variable?

Say I have a variable that is set to some user input. I have no control over what the user will enter.
How would I go about removing all characters that are not in [A-Za-z0-9], spaces, periods, or commas?
proc getUserInput {} {
set timeout 60
send_user "\nEnter user input: "
expect_user {
-re "(.*)\n" {
set userInput $expect_out(1,string)
}
timeout {
exitTimeout "Timed out waiting for user input!"
}
}
return $userInput
}
set rawValue [ getUserInput ]
// massage variable goes here?
set massagedValue "$rawValue"
Not sure if it matters, but I'm using expect 5.45.
$ expect -v
expect version 5.45
Expect is a Tcl extension so you can use all Tcl commands when writing Expect scripts. You can try this in tclsh:
% set v1 "###the string###"
###the string###
% set v2 [regsub -all {[^ .,[:alnum:]]} $v1 ""]
the string
%

calling a proc from inside puts TCL command and put it in file

I am trying to call a proc from inside a puts statement and put it in a file using TCL as follows
set fp_jason [open jason.txt w]
proc MY_FUNCTION { } {
puts "Experiment success"
}
puts $fp_jason " [MY_FUNCTION] "
Output of above script in jason.txt is nothing
Experiment Success is printed in shell
Intead desired output i want jason.txt to contain is
$cat jason.txt
Experiment success
Could to help me get the desired output in jason.txt
Thanks in advance.
When you puts without specifying the channel, you will puts to stdout. If you want to puts a string to a file, you would usually do:
puts $fp_jason "Experiment success"
So if you replace Experiment success by a function, that function has to return the string Experiment success, so automatically this becomes:
set fp_jason [open jason.txt w]
proc MY_FUNCTION { } {
return "Experiment success"
}
puts $fp_jason "[MY_FUNCTION]"
I removed the spaces before and after the function since it doesn't look like you need them. Also, if you are not putting anything more to the file than the result of the function (or anything that might be interpreted as additional arguments by the interpreter), you can safely use:
puts $fp_jason [MY_FUNCTION]
Use quotes if you do something like this because you are using spaces within the string:
proc MY_FUNCTION { } {
return "4"
}
puts $fp_jason "A table has [MY_FUNCTION] legs"
This is wrong because each of the words will be interpreted as arguments to the command puts:
proc MY_FUNCTION { } {
return "4"
}
puts $fp_jason A table has [MY_FUNCTION] legs
You have to apply command substitution by means of using square brackets.
puts [MY_FUNCTION]

tcl "switch -glob" does not match with variable

I encountered this issue on both Solaris and Linux, with tcl version 8.3/8.4
please see the following code:
#!/usr/bin/tclsh
set pattern "this is * and *"
set str "this is tcl and c++"
switch -glob $str {
$pattern {
puts "matched pattern"
}
"this is * and *" {
puts "matched plain text"
}
default {
puts "matched none"
}
}
and the result is "matched plain text".
I though it should have matched the $pattern... is this an incorrect usage of switch, or I am not giving correct pattern for -glob option?
please someone give some idea and it is better if you can tell how to modify the code to make it run with switch and variable.
Thanks!
XM
I believe you're misunderstanding how Tcl parsing/substitution works. Specifically, the glob command is getting the arguments:
1: -glob
2: $str
3: {
$pattern {
puts "matched pattern"
}
"this is * and *" {
puts "matched plain text"
}
default {
puts "matched none"
}
}
The {...} construct "groups" the things inside it into a single, un-substituted (verbatim) piece of data. As such, the third argument to switch contains "$pattern", not the result of replacing $pattern with the value of the pattern variable.
If you need substitution, you need to avoid using curly braces (I'm simplifying). So, if you really want to substitute the value for the pattern in, the easiest way to do it is to use the other form of switch (which passes in each pattern/code block as a separate argument):
switch -glob $str $pattern {
puts "matched pattern"
} "this is * and *" {
puts "matched plain text"
} default {
puts "matched none"
}
As a note, it's almost always a good idea to use the -- (no more flags) flag with switch when you're using a variable substitution for the string to match. That avoids the problem where your $str contains something starting with a -
switch -glob -- $str $pattern {
puts "matched pattern"
} "this is * and *" {
puts "matched plain text"
} default {
puts "matched none"
}