configuring Expect coding to save file with date stamp - expect

I got a simple expect script that I have configured to pull files from a remote system of mine and to save them to my linux box which works great! however every time I run the script it will auto over ride the previous file it saved from the last time I ran the script. Is there a way to add in a way to save the file + (date & time stamp) ?
This is the code I am working with:
#!/usr/bin/expect -f
# connect via scp
spawn scp "username#IP_of_device:" /Linux_box
#######################
expect {
-re ".*es.*o.*" {
exp_send "yes\r"
exp_continue
}
-re ".*sword.*" {
exp_send "PASSWORD\r"
}
}
interact

set remote the_file_name
set date [timestamp -format %Y-%m-%d]
set local "$remote-$date"
spawn scp "username#IP_of_device:$remote" $local
ref: http://www.tcl.tk/man/expect5.31/expect.1.html

Related

Expect script does not work with multiple password requests

I'm trying to execute a perl script to connect to linux hosts generate a snap and then I need to SCP this snap to my local server. So I have to put password 2 times 1 - to login on the servers and 2 - to SCP the file previously generated.
The problem is, my expect script is only working for the first password, would anyone have any idea?
[linuxserver]$ ./mul.sh
spawn perl perlscript.pl
Cleaning up /tmp/logs
Getting logs from server 9.x.x.x using snap
Password: <------ THIS ITERATION WORKS FINE WITH EXPECT
Password: <------ THE 2nd ITERATION DOES NOT WORK AND I HAVE TO ADD THE PASSWORD MANUALLY
[linuxserver]$
export PASSWORD='mypassword'
/usr/bin/expect -c '
spawn perl perlscript.pl
expect {
-nocase "*assword*" {
send "$env(PASSWORD)\r"
exp_continue
}
expect -c "
expect "*assword*"
send \$env(PASSWORD)\r
interact"
}
interact
'
First, use a quoted heredoc when putting an expect script inside a bash script, it reduces quoting hell significantly.
It seems you need to enter the passwords and then just wait for the perl program to end:
put multiple patterns in the same expect command
use the special eof pattern to expect the end of the spawned program.
/usr/bin/expect <<'END_EXPECT'
set timeout -1
spawn perl perlscript.pl
expect {
-nocase "*assword*" {
send "$env(PASSWORD)\r"
exp_continue
}
eof
}
END_EXPECT

Enter sudo password while in expect script

I have an expect code inside bash script, something like this
env input1=${INPUT1} input2=${INPUT2} expect << "EOS"
set timeout -1
spawn ./another_script.sh
expect {
"Input 1" { send -- "$env(input1)\r";exp_continue }
"Input 2" { send -- "$env(input2)\r";exp_continue }
eof
}
EOS
During the execution of another_script.sh I am prompted to enter sudo password, but I'm not able since I'm stil in expect.
What can I do in order for me to be able to enter the sudo password and for the script to continue running after that?
I don't want to save the password in the script and then pass it to expect, but I want to be able to type it in.
The answer from Donal Fellows is the right way to go, but for completeness here is the interact solution you were trying for. The command to use is
expect {
"Input 1" { send -- "$env(input1)\r"; exp_continue }
"Input 2" { send -- "$env(input2)\r"; exp_continue }
"password for" { stty -echo
interact -u tty_spawn_id -o "\r" return
stty echo
exp_continue }
eof
}
The problem you have is that you are running expect with the script on stdin, so interact has trouble unless you use -u tty_spawn_id to get it to work with /dev/tty and the user. You need to set the echo on/off on this tty explicitly, as sudo will only have done it on the pty between the spawned command and expect.
The standard way of handling an event like this is to watch for a prompt for the sudo password, supply it, and then continue. But with a long-running script, you'll want to cache the password within the expect script so that you don't have to go back to the user for it several times over a few hours. Indeed, for usability you probably need to ask for the password up front rather than waiting until the underlying system needs it.
Fortunately, expect includes support for stty so it's an easy thing to add.
env input1=${INPUT1} input2=${INPUT2} expect << "EOS"
# Prompt for password, cribbed/converted from example on expect(1) manpage
stty -echo
send_tty "sudo password: "
expect_tty -re "(.*)\n"
send_tty "\n"
set password $expect_out(1,string)
stty echo
# Rest of the script, with clause for sending the password at the right time
set timeout -1
spawn ./another_script.sh
expect {
"Input 1" { send -- "$env(input1)\r"; exp_continue }
"Input 2" { send -- "$env(input2)\r"; exp_continue }
"assword: " { send -- "$password\r"; exp_continue }
eof
}
EOS
From man sudo: The sudoers policy caches credentials for 15 minutes, unless overridden in sudoers(5).
With that, call sudo -v before calling expect. It checks for the credentials, and if they aren't cached, it will ask for the root password. That way you can enter them before expect and later sudo commands don't need to ask again.
for what you comment I think you have it in cron. In that case you would neeed to run the script as sudo. you can just call it in the sudo crontab. you can access it by running sudo crontab -e

How can I iterate through a range of port numbers, capture the working port and SSH from expect

currently the below script sample works, however i need to iterate through the port range of 8801 and 8899. (must be in the expect section, standard bash for loop will not work here).
#!/bin/bash
testFunction(){
/usr/bin/expect << EOF
#!/usr/bin/expect
spawn ssh admin#localhost -p 8802
expect "password"
send "password\r"
expect "$ "
interact
EOF
}
Try using environment variables. It's quick and dirty but it works:
#!/bin/bash
function testFunction {
local TESTPORT=$1
expect << EOF
spawn sshpass -f x ssh -p ${TESTPORT} admin#localhost
expect "$ "
send -- "w\r"
expect "$ "
send "exit\r"
EOF
}
testFunction 8802
testFunction 8803
I also used sshpass instead of having the ssh password in the expect script. I was using this to test the idea. In this case, the file "x" has the password in it (and is set with highly restrictive permissions).
Can you replace that hard coded 8802 in your HERE doc with a variable? And then you'd be able to put the entire testFunction in a loop, passing in a different port for each iteration.

Shell script with SFTP and mget finishing before downloads are complete

Here is my script:
#!/usr/bin/expect
spawn /usr/bin/sftp user#place
expect "password:"
send "mysecret\n"
expect "sftp>"
send "mget dir/*.csv\n";
expect "sftp>"
send "quit\n"
It starts to download but after a few seconds, it sends the quit command and exits the program before the downloads are complete.
The only way I've successfully made it stop is to throw in an "interact" command but because I want to run this as a cron job, I don't want to just spawn processes that end up sitting.
Is there a way to accomplish this?
edit:
Someone will ask why I don't have a key setup... this is the only method allowed to me by the vendor.
Adding the line set timeout -1 appears to have fixed this problem
The whole script should look as such:
#!/usr/bin/expect
set timeout -1
spawn /usr/bin/sftp user#place
expect "password:"
send "mysecret\n"
expect "sftp>"
send "mget dir/*.csv\n";
expect "sftp>"
send "quit\n"
Well, if you can use Perl:
use Net::SFTP::Foreign;
my $sftp = Net::SFTP::Foreign->new('user#place', password => 'mysecret');
$sftp->mget("dir/*.csv", '.',
on_error => sub {
warn "unable to copy '$_[1]{filename}':".$sftp->error."\n";
});
You will have to install Net::SFTP::Foreign and IO::Pty (also available as packages in several linux distributions).

Command not write in buffer with Expect

I try to backup a Linkproof device with expect script and i have some trouble. It's my first script in expect and i have reach my limits ;)
#!/usr/bin/expect
spawn ssh #IPADDRESS
expect "username:"
# Send the username, and then wait for a password prompt.
send "#username\r"
expect "password:"
# Send the password, and then wait for a shell prompt.
send "#password\r"
expect "#"
# Send the prebuilt command, and then wait for another shell prompt.
send "system config immediate\r"
#Send space to pass the pause
expect -re "^ *--More--\[^\n\r]*"
send ""
expect -re "^ *--More--\[^\n\r]*"
send ""
expect -re "^ *--More--\[^\n\r]*"
send ""
# Capture the results of the command into a variable. This can be displayed, or written to disk.
sleep 10
expect -re .*
set results $expect_out(buffer)
# Copy buffer in a file
set config [open linkproof.txt w]
puts $config $results
close $config
# Exit the session.
expect "#"
send "logout\r"
expect eof
The content of the output file:
The authenticity of host '#IP (XXX.XXX.XXX.XXX)' can't be established.
RSA key fingerprint is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
Are you sure you want to continue connecting (yes/no)? #username
Please type 'yes' or 'no': #password
Please type 'yes' or 'no': system config immediate
Please type 'yes' or 'no':
Like you can see, the result of the command is not in the file. Could you, please, help me to understantd why ?
Thanks for your help.
Romuald
All of your "expect" statements are timing out because the text they are waiting for does not match the text that actually appears. Let's examine the first one or two, the others are all the same.
You say:
expect "username:"
But what it actually receives from ssh is:
The authenticity of host '#IP (XXX.XXX.XXX.XXX)' can't be established.
RSA key fingerprint is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
Are you sure you want to continue connecting (yes/no)?
This does not contain the string "username:" so the expect command will time out, and the script will move on to the next command:
send "#username\r"
We can see it does send that:
Are you sure you want to continue connecting (yes/no)? #username
But that's not a valid answer to this question.
And the rest of the output is the same idea over and over.
As #joefis mentioned, you do need to catch the yes/no from ssh.
I've copied this from my response in serverexchange as its highly relevant here
You will want to avoid using "Password:" if you monitor your strings
during login, you will find that it is not always capitalized.
Changing your expect to -re "(.*)assword:" or "assword:" tends to be
much more effective for catching the line.
If you find that the timings are still too quick you can put a sleep
1; before your send
This is what i use for expect
expect {
#When asked about authenticity, answer yes then restart expect block
"(yes/no)?" {
send "yes\n"
exp_continue
}
"passphrase" { send "\r" }
-re "(.*)assword:" { sleep 1; send -- "password\r" }
-re $prompt { return }
timeout { puts "un-able to login: timeout\n"; return }
eof { puts "Closed\n" ; return }
}
so a couple of things, this will allow for expect to respond to any of these results on a single expect, it will then only continue to more of the code if a return statement is found. I would recommend setting a prompt value as it becomes help full to detect if your commands are complete or your login was indeed successful.

Resources