bash: does expect work with multiple password prompts? - bash

I am currently using expect to pass in passwords so my scripts can run automatically without me having to sit around and type in the same password over and over again.
Important: Please don't comment about how big of a security risk this is or how I should be using ssh keys, I would use those if I could, but the setup I have to work with doesn't allow it.
My code looks like the following:
#!/bin/sh
PASS=mypassword
/usr/bin/expect -c "
spawn python Tools/python/install.py
expect -nocase \"password:\" {send \"$PASS\r\"; interact}
"
The problem I have is that install.py prompts for the same password a dozen times, it appears expect only auto fills the password for the first prompt.
Is there a way to modify the behavior so that it fills in the password all 12 times and not just the first time?

You have to do a little programming. Fortunately the answer is pretty simple:
#!/bin/sh
PASS=MyPassword
export PASS
/usr/bin/expect -c '
spawn python Tools/python/install.py
expect {
-nocase "password:" {
send "$env(PASS)\r"
exp_continue
}
"somthing_else_that_indicates_you're_ready_to_interact"
}
interact
'
Cleaned up the shell quoting a little too.

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

Bash scripting with expect. Set parameters to multiple test boards

I'm working on a small project for school. I'm using 15 or so tuners to emulate a Cell network. I'm by no means well versed in scripting yet. I'm an EE who usually googles until I have some frankencode capable of my purposes.
The goal is the set up all the modules quickly so I thought to automate the process with a script. This requires ssh, and so far I have to manually type in the password each time. This morning I set up a basic test with both Expect and sshpass. In either case I can correctly log in, but not give instructions to the remote machine.
I was reading that sshpass has difficulty with sending remote instruction, correct me if I'm wrong.
/usr/bin/expect << EOF
spawn ssh root#<IP>
expect "(yes/no)?" #Are you sure you want to connect nonsense
send "yes\r"
expect "password"
send "$pass\r"
I tried a few things here to get the device to receive instruction
interact
cat /pathto/config.txt
#or
send "cat /pathto/config.txt
#the real goal is to send this instruction
sqlite3 /database.db "update table set param=X"
EOF
You might as well make it an expect script, not a shell script
#!/usr/bin/expect -f
and then pass the IP address to the script as a command line argument
expect myloginscript.exp 128.0.0.1 the_password
In the expect script, you'll grab that IP address from the arguments list
set ip [lindex $argv 0]
set pass [lindex $argv 1]
(Putting the password on the command line is not good security practice. You can research better methods of passing the password to your expect script.)
To use ssh, you'll be asked "are you sure" only the first time to connect, so let's make that conditional. That is done by letting the expect command wait for several patterns:
spawn ssh root#$ip
expect {
"(yes/no)?" {
send "yes\r"
# continue to wait for the password prompt
exp_continue
}
"password" {
send "$pass\r"
}
}
Once that is sent, you should expect to see your shell prompt. The pattern for this is up to your own configuration but it typically ends with a hash and a space.
expect -re {# $}
Now you can automate the rest of the commands:
send "cat /pathto/config.txt\r"
expect -re {# $}
# note the quoting
send "sqlite3 /database.db \"update table set param='X'\"\r"
expect -re {# $}
At this point, you'll want to log off:
send "exit\r"
expect eof
On the other hand, if you set up ssh private key authentication (see ssh-keygen and ssh-copy-id), you can just do this:
ssh root#IP sqlite3 /database.db "update table set param='$X'"
and not have to get into expect at all.

Why does this expect code not work?

Not sure if this is more of a scripting/unix question or a programming one, but I tried on the unix stackexchange and got no responses, so:
The following expect code seems to work; that is, it appears to enter text in answer to the password prompt. However, the device never actually mounts.
But if I simply enter the command into a shell and type the password in by hand the device mounts successfully.
So I'm curious where the input is actually ending up, as it never seems to 'catch' the password yet doesn't present an error message either? In fact the output looks exactly the same in both instances, but only in the case of running the command and typing the password manually do I see my files appear over the network.
Here is the code:
#!/usr/bin/expect
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=Me
expect "Password: " {
set send_slow {1 .1}
send -s "a_password"
}
UPDATE: Got the help I needed to make it work: had to insert 'expect eof' after sending the password so that it doesn't end prematurely. However I now wish to progress to changing it to 'expect_background' so that I can have the same trigger response to issuing multiple mount commands. The following ends prematurely, and 'expect eof' at the end causes an error:
expect_background "Password:" {
send "a_password\r"
expect eof
}
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=Me
expect eof
What should it look like?
UPDATE: the following code block illustrates the current problem:
expect_background "Password: " {
send "a_password\r"
expect eof
}
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=someone
expect "Password: " {
send "a_password\r"
expect eof
}
#The password prompt gets answered by 'expect' but not 'expect_background'.
#If I delete the last 'expect' and insert 'expect eof' it hangs for a short
#while at the password prompt (around 3 seconds) then exits.
#Why?
Add one more expect statement after sending the password.
send -s "a_password\r"
expect eof
The eof will make the Expect to wait till the end of program.

including conditional statements in expect

I am trying to write a bash script that uses expect to scp a file to remote systems. The expect block that I have so far looks like this
expect -c "
set timeout -1
spawn scp $file user#host:$file
expect "\Are you sure you want to continue connection (yes/no)\"
send -- \"$password\r\"
expect eof
"
The problem is that this handles the case in which the host is not a known host and it asks if I want to continue connecting. I would like to add a an option for the case in which the host is already known and it simply wants the password.
The other issue is that I would like to handle the event in which the password the user entered is not correct. In that case, I would like to have the user reenter the password.
What would be the best way of accomplishing this using bash and expect?
Many thanks in advance!
host is not a known host and it asks if I want to continue connecting
Use: scp -o "StrictHostKeyChecking no" <...>
event in which the password the user entered is not correct
If your script is considered to be interactive, why you are using expect at all? scp can ask and re-ask a password by itself.
Like this:
expect -c "
set timeout -1
spawn scp $file user#host:$file
expect
{Are you sure you want to continue connection (yes/no)} {
send \"yes\r\"
exp_continue
}
{Password: } {
send -- \"$password\r\"
}
}
expect eof
"
The exp_continue command loops back to the containing expect command so that other patterns have a change to be matched.

Writing strings to standard input in bash script

I'm trying to automate a startup of a specific service with bash
When the service is started with init.d (/etc/init.d/openvpn.custom) it is promting for username and then password - and then it connects
The auth-user-pass from-file is not possible with the installed version, and it cannot be upgraded because of dependencies
So i'm trying to write a simple bash scripts that executes the init.d script, sleeps for a bit, inputs the username, returns, sleeping a bit, inputting the password - you'll get the flow.
like http://pastebin.com/qWHX7Di5
I've experimented with echo, but it doesent seem to work
This is for a rather legacy firewall i'm asked to keep connected.
Is this even possible?
I would use expect instead of bash. You can still call it from within bash if you need to do other tasks as well.
In expect, the script would be something like the following (untested):
#!/usr/bin/expect -f
set username "username"
set password "password"
spawn /etc/init.d/openvpn.custom start
expect "Username:"
send "$username\r"
expect "Password:"
send "$password\r"
expect eof
You'd want to change the expect "Username:" & expect "Password:" lines to match the actual login prompts that are output by your init.d script.
See the expect man page for further details.
You can try using a here-doc:
/path/to/init.d << END
$username
$password
END

Resources