Hi here is my expect script:
#!/usr/local/bin/expect -f
set force_conservative 0 ;# set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
set timeout -1
spawn $env(SHELL)
match_max 100000
send -- "ssh IP_addr\r"
expect -exact "password: "
send -- "something\r"
expect -exact "\r"
#expect -exact "Entering server port, ..... type ^z for port menu."
send -- "\r"
expect -exact "login: "
send -- "admin\r"
expect -exact "password: "
send -- "something\r"
expect -exact "something > "
send -- "reboot\r"
expect -exact "REBOOT THE SYSTEM? (y or n): "
send -- "y\r"
expect -exact "SYSTEM REBOOTING!\r"
set no 20
for {set i 1} {$i < $no} {incr i 1} {
send -- "^[-"
}
expect -exact "\/----Enter Password----\\"
expec eof
I want to send escape and hyphen character multiple times until I receive "/----Enter Password-----\ prompt. But I'm receiving following error at this line:
missing close-bracket
while executing
"'send -- "^[-"'
"
("for" body line 2)
invoked from within
"for {set i 1} {$i < $no} {incr i 1} {
'send -- "^[-"'
}"
(file "script_auto.exp" line 31)
I'm newbie to expect. Kindly let me know what does that error mean and how can I resolve it.
expect is an extension of the Tcl language. In Tcl, square brackets are used for command substitution, exactly the way backticks are used in posix shells.
Change
send -- "^[-"
to
send -- {^[-}
The curly braces prevent command substitution, so the open bracket is seen as just a plain character.
Related
I have a expect script that fires commands on remote devices, some of the devices can have credentials issues for which I need to skip is the password prompt repeats after entering the password.
#!/usr/bin/expect -f
set x [list 192.168.1.1 192.168.1.2]
foreach hosts $x {
# spawn ssh sy-nep-oss#$x
sleep 5
spawn ssh -o StrictHostKeyChecking=no sy-nep-oss#$hosts
sleep 5
**expect {"password: "
send -- "abcd\r"
expect "*>"
If the password is incorrect, need to skip this loop iteration and move on to the next item in the list
send -- "cd cfcard:/ \r"
sleep 2
expect "*>"
send -- "cd cfcard2:/ \r"
sleep 2
expect "*>"
sleep 2
send -- "dir log* \r"
expect "*>"
sleep 2
send "cd log \r"
expect {enter code here
"Error: Wrong path *" {
send "cd logfile \r"
}
"logfile*" {
send "cd log \r"
}
}
expect "*>"
sleep 2
send -- "dir log.log \r"
expect "*>"
sleep 2
send -- "system-view \r"
expect "Enter system view*"
sleep 2
send -- "sftp 212.72.10.182 \r"
expect "*username*"
sleep 2
send -- "test\r"
expect "*password:"
sleep 2
send -- "Y\r"
sleep 3
send -- "N \r"
sleep 4
expect "Please select public key type for user authentication*"
send -- "R \r"
sleep 2
expect "*password"
sleep 2
expect "sftp-client>"
sleep 2
send -- "put log.log $hosts.log \r "
sleep 2
expect "sftp-client>"
sleep 2
send -- "quit\r"
sleep 2
send -- "quit\r"
sleep 2
send -- "quit\r"
}
expect eof
I am trying to have an expect script inside a bash to login to a router, execute a command and store output in a text file.
#!/usr/bin/bash
FQDN=$1
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
set FQDN [lindex $argv 0]
set Username "user"
set Password "***$$$"
spawn ssh $Username#$FQDN
expect "*assword:"
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
I am getting the below error message.
system personnel =\r\r\n= may provide the evidence of such monitoring to law enforcement officials. =\r\r\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==\r\r\npassword: "
send: sending "\n" to { exp6 }
expect: does "" (spawn_id exp6) match glob pattern "#"? no
password:
Enter old password:
Based on the error it appears that script is hitting the {return} key "\r" which is not to be sent at password prompt.
I don't have a return once i ssh. Not sure where i am going wrong.
This is my expect script which is working fine. Its only when i code this inside a bash script its failing.
#!/usr/bin/expect -f
set timeout 20
set FQDN [lindex $argv 0]
set Username "user"
set Password "***$$$"
spawn ssh -o "StrictHostKeyChecking no" $Username#$FQDN
expect "*assword: "
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
-Abhi
In a here-doc, variables like $Username and $Password are being expanded by the shell, so they're not seen as literals for Expect to expand. Since those shell variables aren't set anywhere, they're being expanded to null strings. As a result, it's executing ssh #$FQDN and sending an empty password.
You need to escape the $ so that Expect can process them.
You also don't need the set FQDN line in the Expect script, since you're using the shell variable for that.
#!/usr/bin/bash
FQDN=$1
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
set Username "user"
set Password "***$$$"
spawn ssh \$Username#$FQDN
expect "*assword:"
send "\$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
Or you could set them as shell variables, just like FQDN.
#!/usr/bin/bash
FQDN=$1
Username=user
Password="***$$$"
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
spawn ssh $Username#$FQDN
expect "*assword:"
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
Edit: Everything works perfectly without the conditional. Adding in the conditional breaks the script. I've tried conditionals in other simpler scripts and those have worked.
Edit2: Adding the full script as other instances of expect conditionals have worked
I'm trying to modify an autoexpect generated expect script that works when the build succeeds but doesn't handle the build failing.
After significant research, I can't figure out why my expect conditional isn't working
#!/usr/bin/expect -f
set force_conservative 0 ;# set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
set timeout -1
spawn $env(SHELL)
match_max 100000
expect "~\$ "
send -- "sudo su\r"
expect "/home/ubuntu# "
send -- "cd ../../opt/application/"
send -- "\r"
expect "/opt/application# "
send -- "./buildwar.sh \r"
expect {
"BUILD SUCCESSFUL\r" {
send -- "su appuser\r"
expect "/opt/application\$ "
send -- "../../home/ubuntu/shutdown.sh \r"
expect "\"outcome\" => \"success\"}"
send -- "exit\r"
expect "/opt/application# "
send -- "./deploywar.sh \r"
expect "BUILD SUCCESSFUL"
send -- "su appuser\r"
expect "/opt/application\$ "
send -- "../../home/ubuntu/startup.sh \r"
expect "Deployed \"application.war\""
send -- "exit\r"
send -- "exit\r"
send -- "exit\r"
exit 0
}
"BUILD FAILED\r" {
exit 1
}
}
You may need to run with expect debugging on (expect -d). You don't actually show what data you are expecting.
It works for me:
$ expect -c '
log_user 0
spawn sh -c {echo "BUILD SUCCESSFUL"}
expect {
"BUILD SUCCESSFUL\r" {
#various and sundry other code to run
exit 123
}
"BUILD FAILED\r" {
exit 56
}
}
'
$ echo $?
123
$ expect -c '
log_user 0
spawn sh -c {echo "BUILD FAILED"}
expect {
"BUILD SUCCESSFUL\r" {
#various and sundry other code to run
exit 123
}
"BUILD FAILED\r" {
exit 56
}
}
'
$ echo $?
56
The issue was that nesting expect statements doesn't seem to work, so instead of the posted code, the following works:
#!/usr/bin/expect -f
set force_conservative 0 ;# set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
set timeout -1
spawn $env(SHELL)
match_max 100000
expect "~\$ "
send -- "sudo su\r"
expect "/home/ubuntu# "
send -- "cd ../../opt/application/"
send -- "\r"
expect "/opt/application# "
send -- "./buildwar.sh \r"
expect {
"BUILD SUCCESSFUL\r" {
send -- "su appuser\r"
}
"BUILD FAILED\r" {
exit 1
}
}
expect "/opt/application\$ "
send -- "../../home/ubuntu/shutdown.sh \r"
expect "\"outcome\" => \"success\"}"
send -- "exit\r"
expect "/opt/application# "
send -- "./deploywar.sh \r"
expect "BUILD SUCCESSFUL"
send -- "su appuser\r"
expect "/opt/application\$ "
send -- "../../home/ubuntu/startup.sh \r"
expect "Deployed \"application.war\""
send -- "exit\r"
send -- "exit\r"
send -- "exit\r"
You need to match against \n - though it won't matter if you just omit it.
I am trying to write an expect script which would ssh into a server, send sudo su, then check the iptables status and put the output in a log file on the server. Below is the script.
1 #!/usr/bin/expect
2 exp_internal 1
3 log_user 0
4 set timeout 10
5 set password "******"
6
7 spawn /usr/bin/ssh -l subhasish *.*.*.* -p 10022
8
9 expect {
10 -re "password: " {send "$password\r"}
11 -re "$ " {send "sudo su\r"}
12 -re "[sudo] password for subhasish:" {send "$password\r"}
13 -re "# " {send "service iptables status\r"}
14 }
15 set output $expect_out(buffer)
16 send "exit\r"
17 puts "$output\r\n" >> output.log
But while run in debug mode, I am getting error like this;
expect -d testcase
expect version 5.44.1.15
argv[0] = expect argv[1] = -d argv[2] = testcase
set argc 0
set argv0 "testcase"
set argv ""
executing commands from command file testcase
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {24105}
invalid command name "sudo"
while executing
"sudo"
invoked from within
"expect {
-re "password: " {send "$password\r"}
-re "$ " {send "sudo su\r"}
-re "[sudo] password for subhasish:" {send "$password\r"}
..."
(file "testcase" line 9)
Not sure where I am going wrong. It says invalid command name "sudo", I guess this is because expect doesn;t understand these command. How to go around it. Please help. Thanks.
The problem is in this line
-re "[sudo] password for subhasish:" {send "$password\r"}
In Tcl (and hence in expect) the square brackets are the syntax for command substitution (like backticks in the shell). So you either need to escape the brackets or use different quotes that prevent various expansions:
-re {[sudo] password for subhasish:} {send "$password\r"}
That brings up a different issue: are you expecting to see these exact characters? Because you're instructing expect to treat that as a regular expression, and square brackets in a regular expression means a character class, so it will match a single character, either a 's', 'u', 'd' or 'o'. So what you probably need is this:
-re {\[sudo\] password for subhasish:} {send "$password\r"}
or
-ex {[sudo] password for subhasish:} {send "$password\r"}
Thanks Glenn, it's working now. One more reason why it was not working was it requires to sleep between normal login and sudo login, otherwise "sudo su" were being sent before the $ prompt returned, for reference to others, here is the code,
#!/usr/bin/expect -f
#! /bin/bash
set timeout 60
log_user 1
set host *.*.*.*
set password ******
set user subhasish
set logfile output.txt
spawn ssh -p 10022 $user#$host
expect "*?assword:*"
send -- "$password\r"
#log_user 1
sleep 2
expect "$"
send -- "sudo su\r"
expect -gl {*password for subhasish:*}
send -- "$password\r"
expect "#"
send -- "service iptables status\r"
log_file /home/subhasish/output.log
expect "#"
log_file
send -- "exit\r";
send -- "exit\r";
exit 0
I have written a small Expect script to log into a Cisco device; once logged in I want to repeatedly run a command and grep the output.
#!/usr/bin/expect
send_user "Device name: "
expect_user -re "(.*)\n"
set host $expect_out(1,string)
send_user "Username: "
expect_user -re "(.*)\n"
set user $expect_out(1,string)
stty -echo
send_user -- "Password: "
expect_user -re "(.*)\n"
set pass $expect_out(1,string)
stty echo
send_user "show int "
expect_user -re "(.*)\n"
set intf $expect_out(1,string)
send_user "\n"
spawn telnet $host
expect "Username:"
send "$user\r"
expect "Password:"
send "$pass\r"
expect ">"
At this point we have logged into the device, I want to execute the command "show int xxx" repeatedly and grep the output for a specific line. grep isn't in Expect, nor a command like sleep, so I can loop round executing the show int command, grepping out my specific line. How can I mix Expect and Bash like this?
UPDATE: I've pretty much done the script now, I'll post the full script once I get over this last hurdle. A line set bytesnow [exec grep "packets input" \< showint | cut -d \ -f 9] is throwing the error;
child process exited abnormally
while executing
"exec grep "packets input" < \showint | cut -d \ -f 9"
But it works fine in a test script I wrote. The file ./showint is there, running that command on the command line works fine? I can't work out what's wrong?
UPDATE: More investigation (http://wiki.tcl.tk/8489) has shown me that the grep exits with status code 1, which means no pattern matches were found, put the command works just fine from the command line? Even with /full/path/to/showint.
END: I fixed my mistake by realising what a fool I had been, answered below. Thanks all for your help :D
This is what I would do
log_user 0
while(1) {
send -- "sh int $intf | i packets input\r"
set timeout 5
expect {
-re "^ +(\d+) packets" { send_user -- "$expect_out(1,string)" }
timeout { send_user "broke?\n" }
}
}
That'll get you the number of packets input.
This is my first Expect script, its purpose is to give the live (almost, 1 second!) throughput of an interface. The below example gives an interface input speed, because we grep for the line containing "packets input". Change this to "packets output" to get a live output rate for that interface.
#!/usr/bin/expect
# Long delay for those tricky hostnames
set timeout 60
# Prompt user for device name/IP, username, password,
# and interface to query (gi0/2)
send_user "Device name: "
expect_user -re "(.*)\n"
set host $expect_out(1,string)
send_user "Username: "
expect_user -re "(.*)\n"
set user $expect_out(1,string)
stty -echo
send_user "Password: "
expect_user -re "(.*)\n"
set pass $expect_out(1,string)
send_user "\n"
stty echo
send_user "show int "
expect_user -re "(.*)\n"
set intf $expect_out(1,string)
send_user "\n"
spawn telnet $host
expect "Username:"
send "$user\r"
expect "Password:"
send "$pass\r"
expect ">"
set byteslast 0
set bytesnow 0
log_user 0
# Enter a continuous loop grabbing the number of bytes that
# have passed through an interface, each second.
# The different in this number each cycle, is essentially
# how much traffic this interface is pushing.
while { true } {
send "show int $intf\r"
expect ">"
set showint [open "showint" "w"]
puts $showint $expect_out(buffer)
close $showint
set bytesnow [exec grep "packets input" \< showint | cut -d \ -f 9]
if { $bytesnow > $byteslast } {
set diff [expr $bytesnow - $byteslast]
set bps [exec expr "$diff" \* 8]
set kbps [exec expr "$bps" \/ 1000]
} elseif { $bytesnow < $byteslast } {
set diff [expr $byteslast - $bytesnow]
set bps [exec expr "$diff" \* 8]
set kbps [exec expr "$bps" \/ 1000]
} elseif { $bytesnow == $byteslast } {
set kbps 0
}
set byteslast $bytesnow
puts "$kbps Kbps\r"
sleep 1
}
As this is my first Expect script, I have no doubt it could be written more efficiently and clearly (that always the case I find), so if anyone has any pointers on this one I'm all ears! :)
My problem with my exec grep command turned out to be that prior to that, the file I had opened "showint", I hadn't closed, and I was trying to access another file; school boy mistake!