'if else' statement in Expect script - if-statement

I am trying to create a script which will "send" input as per the output received from executing the previous script.
#!/usr/bin/expect --
set timeout 60
spawn ssh user#server1
expect "*assword*" { send "password\r"; }
expect "*$*" { send "./jboss.sh status \r"; }
if [ expect "*running*" ];
then { send "echo running \r"; }
else { send "./jboss.sh start \r"; }
fi
I am trying to do something like this, but I am stuck in the if else statement. How can I fix it?

You can simply group them into single expect statement and whichever matched, it can be processed accordingly.
#!/usr/bin/expect
set timeout 60
spawn ssh user#server1
expect "assword" { send "password\r"; }
# We escaped the `$` symbol with backslash to match literal '$'
# The last '$' sign is to represent end-of-line
set prompt "#|%|>|\\\$ $"
expect {
"(yes/no)" {send "yes\r";exp_continue}
"password:" {send "password\r";exp_continue}
-re $prompt
}
send "./jboss.sh status\r"
expect {
"running" {send "echo running\r"}
-re $prompt {send "./jboss.sh start \r"}
}
expect -re $prompt

Related

Attempt to split string on '//' in Jenkinsfile splits on '/' instead

What is the correct way to tokenize a string on double-forward-slash // in a Jenkinsfile?
The example below results in the string being tokenized on single-forward-slash / instead, which is not the desired behavior.
Jenkinsfile
An abbreviated, over-simplified example of the Jenkinsfile containing the relevant part is:
node {
// Clean workspace before doing anything
deleteDir()
try {
stage ('Clone') {
def theURL = "http://<ip-on-lan>:<port-num>/path/to/some.asset"
sh "echo 'theURL is: ${theURL}'"
def tokenizedURL = theURL.tokenize('//')
sh "echo 'tokenizedURL is: ${tokenizedURL}'"
}
} catch (err) {
currentBuild.result = 'FAILED'
throw err
}
}
The Logs:
The log output from the preceding is:
echo 'theURL is: http://<ip-on-lan>:<port-num>/path/to/some.asset'— Shell Script<1s
[ne_Branch-Name-M2X23QGNMETLDZWFK7IXVZQRCNSWYNTDFJZU54VP7DMIOD6Z4DGA] Running shell script
+ echo theURL is: http://<ip-on-lan>:<port-num>/path/to/some.asset
theURL is: http://<ip-on-lan>:<port-num>/path/to/some.asset
echo 'tokenizedURL is: [http:, <ip-on-lan>:<port-num>, path, to, some.asset]'— Shell Script<1s
[ne_Branch-Name-M2X23QGNMETLDZWFK7IXVZQRCNSWYNTDFJZU54VP7DMIOD6Z4DGA] Running shell script
+ echo tokenizedURL is: [http:, <ip-on-lan>:<port-num>, path, to, some.asset]
tokenizedURL is: [http:, <ip-on-lan>:<port-num>, path, to, some.asset]
Note that the logs show that the string is being tokeni on / instead of on //.
tokenize takes string as optional argument that may contain 1 or more characters as delimiters. It treats each character in string argument as separate delimiter so // is effectively same as /
To split on //, you may use split that supports regex:
theURL.split(/\/{2}/)
Code Demo

using TCL command line, is there a way to stop showing characters?

I'm building a tool were a command with a password needs to be entered.
I want when I enter this command with the password, the command line replaces each character with "*" or " ", so the command and the password will not be observable !
is there such a command that tells the TCL interpreter "from this point, show each character entered as *", and then switch back to regular mode ?
any other suggestion will be valuable too.
In your case, you shall take "full control" over your terminal and disable its default echoing behavior (In UNIX the likes the terminal should be entered into the so-called raw mode)
Then, you can read the characters one-by-one (till max password size or till Enter is pressed) and echo '*' per each pressed character.
You got working code examples both on UNIX and Windows how doing so here
You may want reading also this link echo-free password entry TCL wiki
proc enableRaw {{channel stdin}} {
exec /bin/stty raw -echo <#$channel
}
proc disableRaw {{channel stdin}} {
exec /bin/stty -raw echo <#$channel
}
enableRaw
set c [read stdin 1]
puts -nonewline $c
disableRaw
package require twapi
proc enableRaw {{channel stdin}} {
set console_handle [twapi::GetStdHandle -10]
set oldmode [twapi::GetConsoleMode $console_handle]
set newmode [expr {$oldmode & ~6}] ;# Turn off the echo and line-editing bits
twapi::SetConsoleMode $console_handle $newmode
}
proc disableRaw {{channel stdin}} {
set console_handle [twapi::GetStdHandle -10]
set oldmode [twapi::GetConsoleMode $console_handle]
set newmode [expr {$oldmode | 6}] ;# Turn on the echo and line-editing bits
twapi::SetConsoleMode $console_handle $newmode
}
enableRaw
set c [read stdin 1]
puts -nonewline $c
disableRaw
(Assuming Linux.) By far the easiest way to handle passwords in a terminal is to turn off echoing of input but leave the terminal otherwise in cooked mode. It won't show a * for each entered character, but it does mean that you don't have to handle things like backspace (when a user realises they typed the last couple of characters wrong before hitting Return), etc.
exec /bin/stty -echo <#stdin
set password [gets stdin]
puts ""
exec /bin/stty echo <#stdin
If you've got Tcl 8.6, you can easily make this more robust with this procedure:
proc getPassword {{prompt "Password: "}} {
exec /bin/stty -echo <#stdin
try {
puts -nonewline $prompt
flush stdout
return [gets stdin]
} finally {
puts ""
flush stdout
exec /bin/stty echo <#stdin
}
}
(It's possible to use catch and some scripting to emulate try…finally but it's really annoying.)
If you have a GUI and prefer that, you make a password entry box by setting the -show option to something non-empty (e.g., * to show an asterisk).

Regular expressions in expect and shell script

Friends , im trying to automate a routing using expect , basically its a debug plugin in a special equipment that i need to log some data , to access this debug plugin my company needs to give me a responsekey based on a challengekey , its a lot of hosts and i need to deliver this by friday , what i've done so far.
#!/usr/bin/expect -f
match_max 10000
set f [open "cimc.txt"]
set hosts [split [read $f] "\n"]
close $f
foreach host $hosts {
spawn ssh ucs-local\\marcos#10.2.8.2
expect "Password: "
send "Temp1234\r"
expect "# "
send "connect cimc $host\r"
expect "# "
send "load debug plugin\r"
expect "ResponseKey#>"
sleep 2
set buffer $expect_out(buffer)
set fid [open output.txt w]
puts $fid $buffer
close $fid
sleep 10
spawn ./find-chag.sh
sleep 2
set b [open "key.txt"]
set challenge [read $b]
close $b
spawn ./find-rep.sh $challenge
sleep 3
set c [open "rep.txt"]
set response [read $c]
close $c
puts Response-IS
send "\r"
expect "ResponseKey#> "
send "$response"
}
$ cat find-chag.sh
cat output.txt | awk 'match($0,"ChallengeKey"){print substr($0,RSTART+15,38)}' > key.txt
$ cat find-rep.sh
curl bla-blabla.com/CIMC-key/generate?key=$1 | grep ResponseAuth | awk 'match($0,"</td><td>"){print substr($0,RSTART+9,35)}' > rep.txt
i dont know how to work with regexp on expect so i put the buffer output to a file and used bash script , the problem is that after i run the scripts with spawn looks like my ssh session is lost , does anyone have any tips? should i use something else instead of spawn to invoke my scripts?
expect -re "my tcl compatible regular expression goes here"
Should allow you to use regular expressions.

Using regular expressions with expect

What I'm trying to do is write an expect script that will allow me to search for file A, and if file A is found then send a series of commands. If files B, or C are found then continue on through the script. I keep getting stuck on the regular expression. I run the expression through www.myregextester.com alone with the data I'm searching for and it matches up just fine. If anyone has any experience with this I would really appreciate the help.
send "term length 0\r"
expect "*#"
send "wr mem\r"
expect "*#"
send "dir flash:\r"
expect
if [[ \bc3750-ipservicesk9-mz.122-55.SE7.bin\b ]]; then {
send "conf t\r"
expect "*(config)#"
send "boot system flash:c3750-ipservicesk9-mz.122-55.SE7.bin\r"
expect "*(config)#"
send "exit\r"
expect "*#"
send "reload at 02:00 8 June\r"
expect "System configuration has been modified. Save? [yes/no]:"
send "yes\r"
expect "Proceed with reload? [confirm]"
send "\r"
expect "*#"
send "wr\r"
expect "*#"
send "exit\r
expect eof
elif [[ \bc3750-ipservicesk9-mz.122-55.SE5\b | b\c3750-ipservicesk9-mz.122-55.SE5.bin\b} ]]; then
fi
}
expect {
-re {\mc3750-ipservicesk9-mz\.122-55\.SE7\.bin\M} {
send "conf t\r"
#...
}
-re {\mc3750-ipservicesk9-mz\.122-55\.SE5(?:\.bin)?\M}
}
Expect is an extension of Tcl: you can read about Tcl regular expressions here.

show cdp neighbor in expect script

I'm pretty new to scripting in general. I'm writing an expect script that ssh'es into a Cisco switch, and runs the "show cdp neighbors" command to get a list of all the devices connected to the switch. I then save the output into a variable and exit the ssh session.
I have the username and password being set in the included file.
#!/usr/bin/expect -f
#exp_internal 1
source accountfile
set timeout 10
spawn $env(SHELL)
expect "#"
send "ssh $USERNAME#<hostname>\r"
expect {
"continue connecting" {
send_user "Adding host to ssh known hosts list...\n"
send "yes\n"
exp_continue
}
"Do you want to change the host key on disk" {
send_user "Changing host key on disk...\n"
send "yes\n"
exp_continue
}
"assword:" {
send "$PASSWORD\r"
}
}
expect "#"
send "term len 0\r"
expect "#"
send "show cdp neighbors\r"
expect "#"
set result $expect_out(buffer)
send "exit\r"
expect "#"
So then I want to take $result and look for lines that contain ' R ', and save those lines to a file (R with spaces on either side indicates a router, which is what I'm interested in)
The problem is that if the name of a connected device is long, it puts the name of the device on one line, and then the rest of the data about the device on the next line. So if I match the ' R ' string, I won't get the name of the device, since the name is on the previous line.
Device ID Local Intrfce Holdtme Capability Platform Port ID
...
<device_name_really_long>
Gig 2/0/52 171 R S I WS-C6509 Gig 3/14
<device_name2> Gig 2/0/1 131 H P M IP Phone Port 1
...
Any ideas? there's probably a regex that would do it, but I don't know squat about regex.
SOLVED: thanks to Glenn Jackman
I ended up having to add an expect condition to check if I had a full buffer, so my final code looks like this:
#!/usr/bin/expect
#exp_internal 1
match_max 10000
set expect_out(buffer) {}
set timeout 30
source accountfile
spawn $env(SHELL)
expect "#"
send "ssh $USERNAME#ol2110-3750stack.sw.network.local\r"
expect {
"continue connecting" {
send_user "Adding host to ssh known hosts list...\n"
send "yes\n"
exp_continue
}
"Do you want to change the host key on disk" {
send_user "Changing host key on disk...\n"
send "yes\n"
exp_continue
}
"assword:" {
send "$PASSWORD\r"
}
}
expect "#"
send "term len 0\r"
expect "#"
send "show cdp neighbors\r"
set result ""
expect {
{full_buffer} {
puts "====== FULL BUFFER ======"
append result $expect_out(buffer)
exp_continue
}
"#" {
append result $expect_out(buffer)
}
}
send "exit\r"
expect "#"
set devices [list]
set current_device ""
set lines [split $result "\n"]
foreach line $lines {
set line [string trim $line]
if {[llength $line] == 1} {
set current_device $line
continue
}
set line "$current_device$line\n"
if {[string match {* R *} $line]} {
lappend devices $line
}
set current_device ""
}
puts $devices
set devices [list]
set current_device ""
foreach line [split $result \n] {
if {[llength [split [string trim $line]]] == 1} {
set current_device $line
continue
}
set line "$current_device$line"
if {[string match {* R *} $line]} {
lappend devices $line
}
set current_device ""
}
# devices list should now contain "joined" routers.