including conditional statements in expect - bash

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.

Related

What does it mean to combine interact with return in expect?

I want to use shell and expect to log in to the server with only one command.
But when interact and return are used in one statement, I don't understand what they mean.In addition, "Password" will only appear when SSH link multiplexing is disconnected, so I hope that when "Password" does not appear, it will not send "mypassord".
So how to write scripts to deal with these two situations in a unified way?
When SSH multiplexed links were established, my script was stuck in "expect "Password"".How to deal with it?
In addition, I would like to ask what does the -o parameter mean after the interact? And what is the meaning of using interact and return together?
What's the difference between "send --" and "send "?
I have enabled SSH master connection. Before I log on to the server, I need to first enter the password, and then enter a number to represent which machine I log on to. But when SSH multiplexed links are established, the option of entering passwords becomes redundant.
#!/usr/bin/expect
# ssh command
set cmd [lindex $argv 0]
set relay_num [lindex $argv 1]
set timeout -1
# run ssh command
spawn bash -c "$cmd"
expect "Password*"
send "mypassord\r"
interact -o -nobuffer -re "Option" return
send -- "$relay_num\r"
interact
In combination with the -o switch, that line is looking at the output of the command for the regex "Option". The return will cause the interact command to end and the script continues to the send command (that is explained in the man page).
I think it would be more clear to use expect "Option" instead.

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.

Set OS X password using expect

I'm trying to write a script to update the password of an OS X account to a rotating, centrally-stored value. As a prelude to learning to use tclcurl, I just want to get this prototype script working:
#!/usr/bin/expect
set mgrpassword "newpassword" # this will become a tclcurl command later
spawn passwd manager
expect "New password:"
send "$mgrpassword\n"
expect "Retype new password:"
send "$mgrpassword\n"
puts "\nManager password changed."
exit 0
It runs without errors, but it does nothing; the password for the manager account remains unchanged. I've tried it with both \r and \n but that didn't make any difference. Can anyone see what I'm doing wrong or what steps I'm omitting?
(It will always run with admin rights; that's why there is no 'expect "Old password:"' line.)
Just add one more expect statement at the end, like as follows,
send "$mgrpassword\r"
expect eof
Basically, Expect will work with two feasible commands such as send and expect. If send is used, then it is mandatory to have expect (in most of the cases) afterwards. (while the vice-versa is not required to be mandatory)
This is because without that we will be missing out what is happening in the spawned process as Expect will assume that you simply need to send one string value and not expecting anything else from the session.
Your script can be written in the following way as well which makes it robust with the use of exp_continue. It will make the Expect to run again.
set mgrpassword "newpassword"
spawn passwd manager
expect {
timeout { puts "Timeout happened";exit 0}
"password:" {send "$mgrpassword \r";exp_continue}
eof {puts "Manager password changed"; exit 1}
}
So it turns out that
dscl . passwd /Users/manager [passwordstring]
works a lot better/easier than trying to combine passwd with expect. Hope this helps someone else.

Handle multiple statements in an Expect script

I am new to Expect scripting.
I wrote an Expect script for ssh in a Linux machine, where I am facing a problem in ssh'ing to different Linux machines. Below I have copied the script.
!/usr/local/bin/expect
set LinuxMachine [lindex $argv 0]
spawn ssh root#$LinuxMachine
expect "root#$LinuxMachine's password:"
send "root123\n"
expect "[root#Client_FC12_172_85 ~]#"
send "ls"
interact
When I supply 10.213.172.85 from command line the expect in the 4th line, it reads as "root#10.213.172.85's password:" and logs in successfully
But some Linux will expect
The authenticity of host '10.213.172.108 (10.213.172.108)' can't be established.
RSA key fingerprint is da:d0:a0:e1:d8:7a:23:8b:c7:d8:40:8c:b2:b2:9b:95.
Are you sure you want to continue connecting (yes/no)
In this case the script will not work.
How can I have two Expect statements in one Expect command?
You can use exp_continue in such a case:
expect {
"Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
exp_continue
}
"root#$LinuxMachine's password:" {
send "root123\r"
expect "[root#Client_FC12_172_85 ~]#"
send "ls\r"
interact
}
}
In the above, the Expect block waits for either the yes/no question OR the prompt for password. If the latter, it moves on with providing password, expecting prompt, sending ls command and giving control back.
If the former, it will answer 'yes' and repeat the expect block, ready to find the prompt for a password (or even the yes/no question again, for that matter - not that you will need that).
I would also include some timeouts with meaningful messages in case some expect option does not match as expected, but the above should work.
As a side comment, you don't want to set a root password in a script... I recommend using ssh key authentication.
We like to call it "long log in". There are ssh options that don't check the host keys:
send -- "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no username#host\n"
expect {
"Password" {
send -- "$passwd\n"
}
Part of the Bash script that calls on the expect sets the password:
echo -n "Enter LDAP password: "
read -s passwd
echo

expect inside bash + open(slave pty): bad file number + parent: sync byte write: broken pipe

I am using expect script inside bash script.
I tried to copy a file from remote host to local. The error I face is,
open(slave pty): bad file number + parent: sync byte write: broken pipe
The code:
#!/bin/bash
read -p "Enter username: " username
read -s -p "Enter password: " password
#Expect script
/bin/expect -<<EOD
set SERVERS {100 101 102}
foreach SERVER \$SERVERS {
set timeout -1
spawn scp ${username}#plsa\${SERVER}.corp.com:/log.2011-11-24 log.2011-11-24
expect "*password:"; send "$password\r"
expect eof }
EOD
Thanks
This doesn't exactly answer your question, but why are you wrapping the expect script with bash? If the only thing you're doing in bash is reading a couple of lines of input, you might as well just move that into expect:
#!/usr/bin/env expect
send_user "Enter username: "
expect_user -re "(.*)\n" {set username $expect_out(1,string)}
send_user "Enter password: "
stty -echo
expect_user -re "(.*)\n" {set password $expect_out(1,string)}
stty echo
set SERVERS {100 101 102}
foreach SERVER \$SERVERS {
set timeout -1
spawn scp ${username}#plsa\${SERVER}.corp.com:/log.2011-11-24 log.2011-11-24
expect "*assword:"; send "$password\r"
expect eof }
You've now removed a whole layer of complexity, which might make the actual error easier to debug. Also, note that scp prompts "Password", not "password", so you need to modify your match string as I've done here.
An additional question: can you use ssh key-based authentication for these connections instead of password authentication? This would simplify the entire process -- you wouldn't need to muck about with expect at all in this case. You could just do:
for server in 100 101 102; do
scp -i mykey ${username}#plsa${SERVER}.corp.com:/log.2011-11-24 log.2011-11-24
done
See http://expect.sourceforge.net/FAQ.html#q68
This is one of these "should not happen" errors. For example, the following question in this FAQ mentions that it could be the fault of the C library. Another possibility is that you've run out of some system resource (file descriptors). The most likely reason is that you're calling spawn in a loop and have neglected to call close and wait.
I know that this post is old that re-opening old posts is considered bad fu, but a search for this error message keeps sending Google here, so I think this probably a good place to actually answer it.
In my experience, I found that one of the many possible causes for this has to do with the fact that Expect is making library calls to other programs on the system and, in my case, I was unable to run those secondary programs with my user permissions. I have been unable to find out which library call is causing the issue, but a
username All=(ALL) /path/to/your/script
entered into visudo fixed it for me. Obviously, you don't have to issue blanket permissions, but allowing the user to execute the script with sudo root permissions gave Expect the access it needed. Just thought I would add this with the hope that it would help someone else in the future.

Resources