expect script stop after sending \003 - shell

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}

Related

Linux shell: why "send" command doesn't run

I need some help on below code. I just need to achieve some simpliest task. bakcup and replace some ssl certificate files. but it doesn't seem to work. what is wrong with below code:
import_esx() {
local username=root
local backuptimestamp=$(date +"%m-%d-%y-%I:%M:%S_%p")
/usr/bin/expect << EOF
set timeout 30
spawn ssh -l $username $Ip_Address
expect {
"(yes/no)?" { send "yes\r"; exp_continue }
"*?assword: " { send "$CommonPassword\r"; exp_continue}
}
send_user "Backing up current certificates\r"
send "mv /etc/vmware/ssl/rui.key /etc/vmware/ssl/rui.key.$backuptimestamp\r"
send "mv /etc/vmware/ssl/rui.crt /etc/vmware/ssl/rui.crt.$backuptimestamp\r"
send "mv /etc/vmware/ssl/castore.pem /etc/vmware/ssl/castore.pem.$backuptimestamp\r"
EOF
}
thanks
Jerry
expect {
"(yes/no)?" { send "yes\r"; exp_continue }
"*?assword: " { send "$CommonPassword\r"; exp_continue}
}
you exp_continue on each branch: You have to expect something else so you can stop looping.
Before each send, you should expect something, typically the shell prompt. Also, after the last mv command, you need to send "exit\r" and then expect eof
Assuming your shell prompt ends with "dollar space", you can put it all together:
set prompt {[$] $}
expect {
"(yes/no)?" { send "yes\r"; exp_continue }
"*?assword: " { send "$CommonPassword\r"; exp_continue}
-re $prompt
}
send_user "Backing up current certificates\r"
send "mv /etc/vmware/ssl/rui.key /etc/vmware/ssl/rui.key.$backuptimestamp\r"
expect -re $prompt
send "mv /etc/vmware/ssl/rui.crt /etc/vmware/ssl/rui.crt.$backuptimestamp\r"
expect -re $prompt
send "mv /etc/vmware/ssl/castore.pem /etc/vmware/ssl/castore.pem.$backuptimestamp\r"
expect -re $prompt
send "exit\r"
expect eof

lines from a file as variable in expect script

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.

User input for Expect script

I am new to scripting. How can I write an Expect script to ssh into a device and prompt the user for password? We use a pin + RSA token code as the password so I can't store the password.
#!/usr/bin/expect -f
spawn ssh device1
You will need to use the interact command to type in the password yourself.
The below skeleton should get you on your way:
set prompt "$"
spawn ssh user#host
set timeout 10
expect {
timeout {
puts "Unable to connect"
exit 1
}
#Authenticty Check
"*(yes/no)?" {
send "yes\r"
exp_continue
}
"assword: " {
#Hand control back to user
interact -o "\r" exp_continue
}
"$prompt" {
puts "Cool by the pool"
}
}
I have used this code before to achieve what you wish to achieve. You can take it as a reference point to write your own version of the code.
#!/usr/bin/env expect -f
set passw [lindex $argv 0]
#timeout is a predefined variable in expect which by default is set to 10 sec
set timeout 60
spawn ssh $user#machine
while {1} {
expect {
eof {break}
"The authenticity of host" {send "yes\r"}
"password:" {send "$password\r"}
"*\]" {send "exit\r"}
}
}
wait
#spawn_id is another default variable in expect.
#It is good practice to close spawn_id handle created by spawn command
close $spawn_id
Source: Expect Wiki
The below code will ask the user to enter the password and will use it further in the script where a password will be asked. Just as $pass.
Grabbing password to be used in script further
stty -echo
send_user -- "Enter the password: "
expect_user -re "(.*)\n"
send_user "\n"
stty echo
set pass $expect_out(1,string)

How can I pass a filename with space so it can get the filename via sftp in expect

Here is my script
#!/bin/ksh
RemoteFile=`grep "${File}" ${TempLog}` --> the value of the variable is Site Information_2013-07-04-00-01-26.CSV
/usr/bin/expect << EOF
spawn sftp user#server
expect "password:"
send "123fakepassword\n"
expect "sftp>"
send "cd /home/user/pickup_dir\n"
expect "sftp>"
send "lcd /home/user/Scripts/mart/wmt/RAMDISK0\n"
expect "sftp>"
send "get $RemoteFile\n" ---> I'm trying to pass it here so I can download the file.
expect "sftp>"
send "exit\r"
EOF
But no luck!, How can I pass a filename with double quote so the it will execute as (get "Site Information_2013-07-04-00-01-26.CSV\n") I'm placing it in variable cause the filename change on date. Or A'm I doing it wrong? I know not good to hard code the password but we can't use ssh-key to have a passwordless sftp. Thanks!
You just need to send some quotes. Pick one of
send "get '$RemoteFile'\n"
send "get \"$RemoteFile\"\n"
#!/bin/bash
while true
do
/usr/bin/expect <<EOF
set timeout 300
spawn sftp $remoteUser#$remoteIp
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "$remotePass\r" }
} "*?assword" { send "$remotePass\r" }
expect "sftp> " { send "cd $RemoteFolderPath\r" }
expect "sftp> " { send "mget $remoteFileName\r" }
expect "sftp> " { send "quit\r" }
interact
EOF
done
exit

expect script to ssh returns invalid command name

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

Resources