Expect script to run a shell script in remote server - expect

I am trying to create an expect script to execute a shell script with arguments in remote server, however the script is not getting executed and it is returning the $ prompt in remote server. Here is the script
#!/usr/bin/expect
spawn ssh username#servername
expect "password:"
send "abc\r"
expect "$ "
send "cd /home/abc\r"
send "./script.sh --status arg1 arg2\r"
-- here the script is located in home/abc directory and the shell script ./script needs to be executed with the parameter --status arg1 arg2. Also i need to save the contents of ./script.sh --status arg1 arg2 in a file and email it. Can the mailx command works inside a expect script or i need to call this expect script from a another shell script. Please assist. Thanks

Try something quick and risky:
#!/usr/bin/expect -f
set password your_password
set timeout 100
spawn scp -q /home/abc/script.sh username#server:/tmp
expect {
"(yes/no)?" {send "yes\r"; exp_continue}
"password:" {send "$password\r"; exp_continue}
"lost connection" {puts "ERROR: lost connection"}
"No route to host" {puts "ERROR: no route to host"}
timeout {puts "ERROR: timeout"}
}
spawn ssh username#server
expect {
"(yes/no)?" {send "yes\r"; exp_continue}
"password:" {send "$password\r"; exp_continue}
"username#server" {send "sh /tmp/script.sh; exit\r";exp_continue }
"password for username:" {send "$password\r"; exp_continue}
"password:" {send "$password\r"; exp_continue}
"lost connection" {puts "ERROR: lost connection"}
"No route to host" {puts "ERROR: no route to host"}
timeout {puts "ERROR: timeout"}
}
Variable password put there to run script automatically.
Also you see "$password\r" as reference to set variable.
spawn scp -q /home/abc/script.sh username#server:/tmp copies your script to /tmp in remote server. -q flag disables the progress meter.
spawn ssh username#server connects to your server.
sh /tmp/script.sh runs script copied in the previous step.
Also, I send e-mails using mutt

Related

SFTP file transfer between two server and running this script from 3rd server

I have developed a script using TCL expect. The use of the script is - if user runs it from server A, it will check sftp file transfer between server B and server C. Below is my code:
#!/usr/bin/expect -f
lassign $argv 1 2
spawn ping -c 2 -i 3 -W 1 $1
expect {
" 0%" {puts "Source is rechable!"}
" 100.0%" {puts "Source is not rechable.Please restart IPSEC and check!";exit 1}
}
#SSH to remote server $1
spawn ssh -o StrictHostKeyChecking=no root#$1
expect {
"Password:" {send "password\r";exp_continue}
"*#" {send "ssh successful\r";exp_continue}
}
send "\n"
#Creating a file in remote server which will be transferred via sftp
send "touch /tmp/mfile\n"
expect "#"
send "chmod 755 /tmp/mfile\n"
expect "#"
#sftp to server$2
spawn sftp -o StrictHostKeyChecking=no root#$2
expect {
"Password:" {send "Training\r";exp_continue}
"sftp>" {send "sftp successful\r";exp_continue}
}
send "\n"
#sending remote file
send "put /tmp/mfile\n"
send "\n"
sleep 2
send "File send to Remote Server successfully\n"
expect "sftp>"
send "cd /root/\n"
expect "sftp>"
send "rename mfile mfile_1\n"
expect "sftp>"
#sending back the file
send "get mfile_1 /tmp\n"
expect "sftp>"
sleep 2
send "quit\n"
send "exit\n"
The issue is the file is getting transferred to the Server C from the Server B with this code, but the file is not sent back to Server B. I am yet to add other logic in the code but first wanted to check if the basic code works. Any clue regarding file transfer back would be helpful.

How to use expect in Shell script like nested If Else loop?

I have written below Shell script which is intended to get Model name from remote host by doing SSH and executing the command.
#!/bin/bash
> output.csv
IFS=","
echo "IP,Model Name" >> output.csv
while read ip
do
#echo "Current IP is: $ip"
model=expect -c 'spawn ssh username#'"$ip"' "show version | in cisco"; expect -re "The.*(yes/no)?"; send "yes\r"; expect -re ".*UNAUTH.*password:"; send "password\r";' | grep cisco
echo "$ip,$model" >> output.csv
done < Check_SSH.csv
When I execute below command manually, then it gives expected model name as output.
Command:
expect -c 'spawn ssh username#'"$ip"' "show version | in cisco"; expect -re "The.*(yes/no)?"; send "yes\r"; expect -re ".*UNAUTH.*password:"; send "password\r";' | grep cisco
But when its put into script like above it doesn't produce any output.
Also, there are MOTD (Message of the day) configured on most of the servers and "The authenticity of host..." message to adding server into .ssh/known_hosts, So I tried to handle them in script but Expect is failing to handle the situation when MOTD doesn't appear or when remote is already present in .ssh/known_hosts.
Any help is highly appreciated to get this script running.
Expected output:
IP,Model Name
8.8.8.8,C9407R
8.8.8.1,C9407R
8.8.8.2,C9407R
8.8.8.3,C9407R
First, you're missing the Command Substitution syntax to execute the expect code:
model=$(expect -c ...)
# ....^^.............^
Next, to optionally expect patterns, you need the expect {patt1 action1 patt2 action2 ...} form:
expect -c '
spawn ssh username#'"$ip"' "show version | in cisco"
expect {
-re "The.*(yes/no)?" {send "yes\r"; exp_continue}
-re ".*UNAUTH.*password:" {send "password\r"; exp_continue}
eof
}
'
That way, expect can match any of the patterns. The exp_continue command "loops" within the same expect command so you can match more than one of them. The eof pattern matches when ssh connection closes after the "show version ..." command has finished.
Newlines for readability.
Putting this together:
model=$(
expect -c '
spawn ssh username#'"$ip"' "show version | in cisco"
expect {
-re "The.*(yes/no)?" {send "yes\r"; exp_continue}
-re ".*UNAUTH.*password:" {send "password\r"; exp_continue}
eof
}
' | grep -i cisco
)
I have a feeling that there's more you need to do in the grep part, but you didn't show the output of just the expect command.
update:
use spawn -noecho ssh ... so expect will not print the spawn command.
then, you'll get whatever output ssh needs to show for the login process, and then the "show" command output:
if you're expecting exactly 1 line of output, you might want to change grep to tail -n 1.
otherwise, show the output you get and we can help you filter out the noise.
update 2: filtering out the noise
I'm going to assume that the regex pattern cisco (.*) processor is what you need to match:
model=$(
expect -c '
log_user 0
spawn ssh username#'"$ip"' "show version | in cisco"
expect {
-re "The.*(yes/no)?" {send "yes\r"; exp_continue}
-re ".*UNAUTH.*password:" {send "password\r"; exp_continue}
-re "cisco (.*) processor" {puts $expect_out(1,string)}
}
expect eof
'
)
log_user 0 turns off the spawned process's ability to write to stdout. Expect can still capture its output though.

Why does my Expect script only echo the command not running?

I'm trying to automate some ssh process. I have my Expect code. But my Expect code only echos/prints out the command. It doesn't actually run the command.
#!/usr/bin/expect -f
set timeout 10
set usrnm "aaaaaa"
set pwd "pppppp"
set addr1 "xxx.cloud.xxx.com -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
set addr2 "xxx.xxxx.xxxx.com"
spawn ssh $usrnm#$addr1
expect {
"(yes/no)?" {send "yes\r";exp_continue}
"password: " {send "$pwd\r"}
}
expect "*#"
send "ssh $usrnm#$addr2\r"
expect {
"(yes/no)?" {send "yes\r";exp_continue}
"password:" {send "$pwd\r"}
}
expect "*#"
send "cd /tmp/myself/folder\r"
expect "*#"
send "./run_engine.sh test.py\r"
expect eof
#interact
So if I do
expect my_expect.exp
it just prints the command:
spawn ssh aaaaaa#xxx.cloud.xxx.com -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
(10s later)
ssh aaaaa#xxx.xxxx.xxxx.com
(10s later)
cd /tmp/amz337/COAFALV
(10s later)
./run_engine.sh test.py
(exit)
What's wrong with my script?
Because Tcl (and thus Expect) does not change the word boundaries when variables get substituted. You are trying to log into the host named exactly:
xxx.cloud.xxx.com -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
spaces and all.
Logically, it does not make sense to put ssh options into a variable that holds the address. May I suggest:
set addr1 "xxx.cloud.xxx.com"
set addr2 "xxx.xxxx.xxxx.com"
set ssh_opts($addr1) {-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no}
set ssh_opts($addr2) {}
Then
spawn ssh {*}$ssh_opts($addr1) $usrnm#$addr1
The {*} syntax is Tcl's "splat" operator that splits a word with spaces into the individual words. See https://tcl.tk/man/tcl8.6/TclCmd/Tcl.htm rule #5.
Later, when you connect to the second machine, you're interpolating into a string, so the splat is not necessary:
send "ssh $ssh_opts($addr2) $usrnm#$addr2\r"
You might want to catch timeout events and abort the script:
expect {
timeout {error "timed-out connecting to $addr1"}
"(yes/no)?" {send "yes\r"; exp_continue}
"password: " {send "$pwd\r"}
}
At the end of your script, after the run_engine script completes, you're still connected to addr2, so expect eof will not actually detect EOF on the spawned process. You'll timeout after 10 seconds and the Expect process will exit. For tidiness, you should:
send "./run_engine.sh test.py\r"
expect "*#"
send "exit\r"
# This prompt is from addr1
expect "*#"
send "exit\r"
# _Now_ the spawned ssh process will end
expect eof
If you think the run_engine script will take longer than 10 seconds, you should adjust the timeout variable before sending that command.
Also, while developing an Expect script, you should turn on debugging:
exp_internal 1
That will show you what's going on behind the scenes, especially when it comes to seeing if your patterns are matching.

How to make expect nested in the bash script properly?

Here is the expect file which can run succesfully.
#!/usr/bin/expect -f
set host vps_ip
set user test
set loginpwd passwd
set adminpwd passwd
set timeout 300
set prompt "#|>|\\\$"
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
expect -nocase "password:"
send "$loginpwd\r"
expect eof
spawn ssh $user#$host
expect -nocase "password:"
send "$loginpwd\r"
expect -re $prompt
send "su\r"
expect "assword:"
send "$adminpwd\r"
expect -re $prompt
send "mysql -u root -pxxxx wpdatabase < /tmp/wpdatabase_backup.sql\r"
expect eof
Here is the bash file:
mysqlword="xxxx"
mysqldump -u root -p$mysqlword wpdatabase > /home/wpdatabase_backup.sql
Now i want to make the expect file nested in the bash file because the bash file is more complicated than expect file ,most lines in the bash file were omitted.
code:
#!/usr/bin/bash
mysqlword="xxxx"
mysqldump -u root -p$mysqlword wpdatabase > /home/wpdatabase_backup.sql
/usr/bin/expect <<EOD
set host vps_ip
set user test
set loginpwd passwd
set adminpwd passwd
set timeout 300
set prompt "#|>|\\\$"
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
expect -nocase "password:"
send "$loginpwd\r"
expect eof
spawn ssh $user#$host
expect -nocase "password:"
send "$loginpwd\r"
expect -re $prompt
send "su\r"
expect "assword:"
send "$adminpwd\r"
expect -re $prompt
send "mysql -u root -pxxxx wpdatabase < /tmp/wpdatabase_backup.sql\r"
expect eof
<<EOD
An error occur :
spawn scp /home/wpdatabase_backup.sql #:/tmp
ssh: Could not resolve hostname : Name or service not known
lost connection
send: spawn id exp6 not open
while executing
"send "\r""
Quote the terminator:
cat<<'EOD'
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
EOD
Result:
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
But how to make the output of result run in bash?
/usr/bin/expect <<cat<<'EOD'
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
EOD
The code can't run!
This is because in a here-document with an unquoted terminator like EOD, parameter substitution is performed by the shell. You can see that if you replace /usr/bin/expect with cat:
$ cat<<EOD
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
EOD
Result:
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql #:/tmp
So, how to avoid parameter substitution? Quote the terminator:
$ cat<<'EOD'
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
EOD
Result:
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp

perl program not running thorough automated script until it is executed manually first

i am writing code to automate some steps . First it is required to switch user and then run a perl script. Here is my code
if [ -a /try/Test ]
then
su trial -c ". /try/.profile Test"
expect -c 'spawn try1;
send "3\r";
send "1\r";
send "show\r";
interact';
fi
try1 is my perl program which i am trying to call.This script throws this error
couldn't execute "try1": no such file or directory
while executing
"spawn try1"
but once i do this step manually and then run this script then this script runs without nay error.
I think you've already asked about it (and I did answer, didn't I)?
Here's the basic skeleton (make sure to add error/timeout/unexpected output handling):
# collect password
stty -echo
send_user -- "Password: "
expect_user -re "(.*)\n"
send_user "\n"
stty echo
set pass $expect_out(1,string)
spawn sudo sh;
expect -re ": *$";
send -- "$pass\r"
expect -re "\$ *$";
send "echo SETTING PARAMS\r";
expect -re "\$ *$";
send "echo RUNNING MY COMMAND\r";
expect -re "\$ *$";
interact

Resources