lines from a file as variable in expect script - shell

Need some help creating a loop in expect script where the variable is extracted from lines in a file.My current expect script is like below;
#!/usr/bin/expect -f
set i [open "samplelist"]
set hosts [split [read $i] "\n"]
set timeout -1
foreach host $hosts {
spawn /usr/bin/ssh appadm#$host
expect "appadm#$host:~>"
send "su epos\r"
expect "Password:"
send "pa55w0rd\r"
expect "epos#$host:/home/appadm>"
send "grep playlist /appl/epos/bin/cron.epos\r"
expect "epos#$host:/home/appadm>"
send "exit\r"
expect "appadm#$host:~>"
send "exit\r"
}
expect eof
close
However when i ran this script it does not terminate correctly after reading the last line of the file

Final modified expect script after being guided by Glenn
#!/usr/bin/expect -f
set i [open "samplelist"]
set hosts [split [read -nonewline $i] "\n"]
set timeout -1
foreach host $hosts {
spawn /usr/bin/ssh appadm#$host
expect "appadm#$host:~>"
send "su epos\r"
expect "Password:"
send "p#ssw0rd\r"
expect "epos#$host:/home/appadm>"
send "grep playlist /appl/epos/bin/cron.epos\r"
expect "epos#$host:/home/appadm>"
send "exit\r"
expect "appadm#$host:~>"
send "exit\r"
expect eof
}
The scripts reads a list of hosts from a file, loop connects and run a command to the remote host until the end of the list.

Related

Loop in Except Script Variable Getting Value from Text File

I have the following script:
#!/usr/bin/expect -f
# Set variables
set hostname [lindex $argv 0]
set username [lindex $argv 3]
set password [lindex $argv 1]
set devname [lindex $argv 4]
set enablepassword [lindex $argv 2]
set conft 0
# Log results
log_file -a ~/scripts/ExpectScripts/9120CiscoAPs/results.log
# Announce which device we are working on and at what time
send_user "\n"
send_user ">>>>> Working on $hostname $devname # [exec date] <<<<<\n"
send_user "\n"
# Don't check keys
set timeout 30
spawn ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 $username\#$hostname
expect "User:"
send "$username\n"
expect "Password:"
send "$password\n"
expect "*>"
set f [open "aprebootlist2.txt"]
set aps [split [read $f] "\n"]
close $f
foreach ap $aps {
send "config ap reset $ap\n"
expect {
"*n)" {send "y\n"}
"*invalid." {send "\r"}
}
expect eof
close
}
send "\r"
expect "*>"
send "logout\n"
expect "*(y/N)"
send "n\r"
exit
This should log into a cisco wireless controller and basically run the command "ap config reset apvariable" a bunch of times. I tried to create a loop getting the variable for ap name from a text file. The script logs into the controller, reads the first name in the file, than fails with the below error. What am I doing wrong?
Error is:
Cisco Controller) >send: spawn id exp6 not open
while executing
"send "config ap reset $ap\n""
("foreach" body line 2)
invoked from within
"foreach ap $aps {
send "config ap reset $ap\n"
expect {
"*n)" {send "y\n"}
"*invalid." {send "\r"}
}
expect eof
..."
(file "./rebootapcommands2.exp" line 29)

Hitting a return at password prompt in expect script instead of sending password

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

Expect within bash script

I am trying to implement an expect script into a bash script. Bear with me since I am fairly new to bash/expect.
Here is the expect script that works as intended:
log_user 0
file delete foo.txt
set fh [open foo.txt a]
set servers {xxx#server1 xxx#server2}
foreach s $servers {
spawn ssh $s
expect "password: "
send "PASSWORD\r"
expect "$ "
send "grep "something" /some/log/file.log"
expect "$ " { puts $fh "$expect_out(buffer)"}
send "exit\r"
}
close $fh
Now, I am hoping to include this expect script in a bash script but it is not working as intended.
Here is what I have so far:
#!/bin/bash
XYZ=$(expect -c "
file delete foo.txt
set fh [open foo.txt a]
set servers {xxx#server1 xxx#server2}
foreach s $servers {
spawn ssh $s
expect "password: "
send "PASSWORD\r"
expect "$ "
send "grep "something" /some/log/file.log"
expect "$ " { puts $fh "$expect_out(buffer)"}
send "exit\r"
}
close $fh
")
echo "$XYZ"
The error I am getting is:
command substitution: line 42: syntax error near unexpected token `('
command substitution: line 42: `expect "$ " { puts $fh "$expect_out(buffer)"}'
I'm open to any other ways to implement this! :)
You can use /usr/bin/expect -c to execute expect commands :
#!/bin/bash
/usr/bin/expect -c '
file delete foo.txt
set fh [open foo.txt a]
set servers {xxx#server1 xxx#server2}
foreach s $servers {
spawn ssh $s
expect "password: "
send "PASSWORD\r"
expect "$ "
send "grep "something" /some/log/file.log"
expect "$ " { puts $fh "$expect_out(buffer)"}
send "exit\r"
}
close $fh
'
Bertrand's answer is one way to solve your question, but does not explain the problem with what you were doing.
Bash attempts to expand variables inside double quoted strings, so your expect script will see blank spaces where you want to see $servers, $s, and $fh. Further, you have triply-nested sets of double-quoted strings going on that will cause all sorts of problems with parsing the arguments to expect.
It's a matter of opinion, but I think when something gets to a certain point where it's considered a program of its own, it should be separated into a separate file.
#!/usr/bin/expect
log_user 0
file delete foo.txt
set fh [open foo.txt a]
set servers {xxx#server1 xxx#server2}
foreach s $servers {
spawn ssh $s
expect "password: "
send "PASSWORD\r"
expect "$ "
send "grep 'something' /some/log/file.log"
expect "$ " {
puts $fh "$expect_out(buffer)"
}
send "exit\r"
}
close $fh
Make sure it's executable, and then call it from your bash script:
#!/bin/bash
/usr/local/bin/my_expect_script
(To do this really properly, you should set up public key authentication and then you can get rid of expect altogether by running ssh server "grep 'something' /some/log/file.log" directly from bash)

[Expect Script]Separate log files for each device

My code is generated for one log file output from multiple devices.
How could we separate each device for each log file output?
Here is my code:
#!/usr/bin/expect -f
#Slurp up the input file
set fp [open "ip.txt" r]
# To avoid empty lines, 'nonewline' flag is used
set file_data [read -nonewline $fp]
close $fp
set prompt ">"
log_file -noappend router_status.txt
foreach ip [split $file_data "\n"] {
puts "Router $ip Interface Status"
spawn telnet $ip
expect "Username:"
send "username\r"
expect "assword:"
send "password\r"
expect $prompt
# To avoid sending 'Enter' key on huge configurations
send "show interface description\r"
expect {
-ex "---(more" { send -- " "; exp_continue }
"*>" { send "exit\r" }
}
set timeout 3; # Reverting to default timeout
# Sending 'exit' at global level prompt will close the connection
expect eof
}
You can simply achieve it by changing the log_file
foreach ip [split $file_data "\n"] {
puts "Router $ip Interface Status"
spawn telnet $ip
# Altering 'log_file' for each ip
log_file -noappend router_${ip}_status.log
# Your further code here...
}

expect script stop after sending \003

Need some assistance on an expect script where it would not continue. The function of the script is to connect on remote hosts, change user then ran a command. But i place a else statement that when the change user prompts to update password it will just skip that and continue on the next host. However the script stops on the point where i send \003.The expect script is indicated below;
#!/usr/bin/expect -f
set i [open "eposhosts"]
set hosts [split [read -nonewline $i] "\n"]
foreach host $hosts {
spawn /usr/bin/ssh -q appadm#$host
expect "appadm#$host:~>"
send "su epos\r"
expect "Password:"
send "password\r"
expect {
"epos#$host:/home/appadm>" {send "grep playlist /appl/epos/bin/cron.epos\r"; exp_continue}
"(current) UNIX password:" {send "\003"; exp_continue}
}
expect {
"epos#$host:/home/appadm>" {send "exit\r"; exp_continue}
"[appadm#$host ~]" {send "exit\r"}
}
expect "appadm#$host:~>"
send "exit\r"
expect eof
}
From the error message, it looks like the problem is because of un-escaped open square bracket [. If you want to match literal open square bracket, then it should be,
"\\\[appadm#$host ~]"
The reason is, [ is special to both Tcl and Expect. Unless escaped, it will treated as a command call.
Change your code as,
expect {
"epos#$host:/home/appadm>" {send "exit\r"; exp_continue}
"\\\[appadm#$host ~]" {send "exit\r"}
}
Note : We don't need to escape the close square bracket.
The value \003 is controlC (^C), which is likely to be an interrupt character on receiving terminal. If expect sends that as expected, it can very well stop a program.
Responding to comment by #Dren:
If you send newlines, the change-password prompt will probably reject that (no empty passwords...) and will not stop your script.
For instance
{send "\n\n\n"; exp_continue}
rather than
{send "\003"; exp_continue}

Resources