How to automate the process with expect tool? - shell

I want to automate a process with expect :
1.login with a common user in ssh
2.su to root
3.make diretory /home/test
set user xxxx
set host yyyy
set password1 pass1
set password2 pass2
set timeout 60
spawn ssh $user#$host
expect "*assword:*"
send "$password1\r"
spawn su
expect "*assword:*"
send "$password2\r"
send "mkdir /home/test"
close
How to fix the codes to automate my process?

Basically, expect will work with two feasible commands such as send and expect. In this case, 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, making the script exits and causing the failure.
Apart from the commands being missed, I see one issue with su command. I hope your intention is to create directory with su command execution in the remote host, not in local. So, assuming that, you just have to send the command as su to the server. Your current code will spawn su in the local machine.
#!/usr/bin/expect
set host xxx.xxx.xxx.xxx
set user dinesh
set loginpwd welcome
set adminpwd root
set timeout 60
set prompt "#|>|\\\$"; # We escaped the `$` symbol with backslash to match literal '$'
spawn ssh $user#$host
expect -nocase "password:"
send "$loginpwd\r"
expect -re $prompt
send "su\r"
expect -nocase "password:"
send "$adminpwd\r"
expect -re $prompt
send "mkdir /home/test\r"
expect -re $prompt
send "logout\r"
expect eof
eof will wait for the closure of the ssh session.

Related

Why does my expect script exit prematurely?

Here is my except script:
#!/usr/bin/expect
spawn openvpn --config peter.ovpn
expect -exact "Enter Private Key Password: "
send -- "mypassword\r"
I run it and see OpenVPN ask for my client password. But the script exits, apparently without ever sending the password. When I try with an incorrect password it is the same (no incorrect password message). It is also exactly the same result if I delete the send -- "mypassword\r" line from the end of the expect script.
It's my first expect script so probably my syntax is wrong. Or could it be that OpenVPN is kicking me off for using an expect script to connect?
Your syntax is fine. The problem is the script has no more commands to run after you send the password, so the expect script exits and that kills openvpn.
What do you need to do after you send the password?
If you just need to keep openvpn running, then do this:
#!/usr/bin/expect
spawn openvpn --config peter.ovpn
expect -exact "Enter Private Key Password: "
send -- "mypassword\r"
set timeout -1
expect eof
-1 means "infinite", and expect eof means you are waiting for the spawned process to exit before the expect script can exit.

Why '\n'('\r') does not work in my expect script

I want to input y+enter to reply the question while executing copy tftp:something.
The script will send y, but \n does not work. It will stay (y/n)y and keep there without exiting or doing something else. I have tried \r, and the result was the same. Does anyone know the reason?
#!bin/bash
expect -c "
set timeout -1
spawn telnet x.x.x.x
expect \"username\"
send \"user\n\"
expect \"password\"
send \"pw\n\"
expect \"model\"
send \"copy tftp:something\n\"
expect \"(y/n)\"
send \"y\n\"
expect eof
"
exit 0
I prefer using \r to "hit enter".
Second, your entire bash script is expect, so remove the outer bash layer.
#!/usr/bin/expect -f
set timeout -1
spawn telnet x.x.x.x
expect "username"
send "user\r"
expect "password"
send "pw\r"
expect "model"
send "copy tftp:something\r"
expect "(y/n)"
send "y\r"
expect eof
If you have more logic in the bash part, to avoid quoting hell use a heredoc:
#!/bin/bash
expect <<'END_EXPECT'
set timeout -1
spawn telnet x.x.x.x
expect "username"
send "user\r"
expect "password"
send "pw\r"
expect "model"
send "copy tftp:something\r"
expect "(y/n)"
send "y\r"
expect eof
END_EXPECT
exit 0

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.

Expect script for checking ssh connection for a list of ips

Can anyone help me in creating an expect script to just do an SSH on a list of servers and check if it was fine. I do not need to interact, do not need to fire any command on each server, I just want to do an ssh and come out, with a return code mentioning whether it was successful or not.
Expect is important here, as I do not have an option to setup a passwordless connection. Also there is a possibility that passwordless connection is setup on some of those servers.
I tried something like:
#!/usr/local/bin/expect
set timeout 10
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set prompt "(>|%|\\\\\\\$|#|]|) \$"
spawn ssh "$user\#$ip"
expect "Password:"
send "$password\r"
send "echo hello\r"
expect "hello"
send "exit\r"
But this gets stuck on the first server, and does nothing after that.
Thanks,
Piyush
A generalized idea can be having a procedure to spawn the ssh and close the connection which will maintain the connections local to the scope, so that global spawn_id won't get affected at all.
#!/usr/bin/expect
proc isHostAlive {host user pwd} {
# We escaped the `$` symbol with backslash to match literal '$'
# Commonly used prompt types
set prompt "#|%|>|\\\$"
set timeout 60
spawn ssh $user#$host
expect {
timeout {return FAIL}
"(yes/no)" {send "yes\r";exp_continue}
"password:" {send "$pwd\r";exp_continue}
-re $prompt
}
set msg "Hello World"
send "echo $msg\r"
expect {
timeout {return FAIL}
"\r\n$msg"
}
send "exit\r"
expect {
timeout {return FAIL}
eof
}
return PASS
}
# Lists to maintain the each host's information
set serverList {server1 server2 server3}
set userList {user1 user2 user3}
set pwdList {pwd1 pwd2 pwd3}
# Looping through all the servers and checking it's availability
foreach server $serverList user $userList pwd $pwdList {
puts "\n==>Status of $server : [isHostAlive $server $user $pwd]\n"
}
With exp_continue, we can handle even if any host does not have password. Basically exp_continue will cause the expect to run again. So, among the mentioned phrase whichever comes, it will be handled. i.e. if expect sees (yes/no), it will send yes, if expect sees password, it will send the password value and so on. Then expect will continue to wait for the whole set of phrases again.
The reason why I have added yes/no is because if suppose the host's RSA fingerprint needs to be saved.
After successful login, I am echoing Hello World and expecting for the echoed message. If you have noticed, I have used \r\n$msg in the expect statement. Why do we need \r\n here ?
Here is why. Whenever we send command that will be seen by the expect also and it will try to match against that too. If it matched, it will proceed as such.
Sample echo command output
dinesh#dinesh-VirtualBox:~/stackoverflow$ echo Hello World
Hello World
dinesh#dinesh-VirtualBox:~/stackoverflow$
The string we want to expect is already available in the send command. So, to make sure the matching expect string is only from the actual echoed response, I have added \r\n which will help us in matching what is necessary.
Then at last of the proc, I am sending exit command which will close the ssh connection and to match the same eof (End Of File) is used. In all sort of failure cases, the procedure will return FAIL.

bash script which downloads a file with sftp

I need to make a sftp connection with a password and download a file. There is an ip restriction so first of all i should make a ssh connection. I wrote a script but it stucks after connecting with ssh.
Note: i also tried doing it with an expect script but it also didn't work.
#!/usr/local/bin/
ssh test#test1.t.com
lftp sftp://test2:123456#test2.com
get "file.xls"
Edit: You can also see my expect code here.
#!/usr/local/bin/expect -f
expect -c "
spawn ssh test#test1.t.com
expect \"test\#test1\:\~\$\"
spawn sftp test2#test2.com
expect \"*assword:\"
send \"123456\r\"
expect \"sftp\>\"
send \"get file.xls\r\"
expect \"sftp\>\"
exit 1
";
I'm not sure exactly what you're trying to accomplish here. First, I'll address the problems in your expect script. Since your shebang line invokes expect, you don't need to wrap the expect body in a call to expect. That gets rid of all the backslashes. Next, you have 2 spawn calls, which raises questions about you're intent. I'm going to assume that you want to ssh to test1, then grab the file from test2 so the file exists on test1. This assumption changes the 2nd spawn to a plain send command.
#!/usr/local/bin/expect -f
set shell_prompt "test#test1:~$"
set sftp_prompt "sftp>"
spawn ssh test#test1
expect $shell_prompt
send "sftp test2#test2\r"
expect "*assword:"
send "123456\r"
expect $sftp_prompt
send "get file.xls\r"
expect $sftp_prompt
send "exit\r"
expect $shell_prompt
send "exit\r"
expect eof
Now, you can scp the file to your local machine. Let's put those 2 steps into one shell script:
#!/bin/sh
expect <<'EXPECT_SCRIPT'
set shell_prompt "test#test1:~$"
set sftp_prompt "sftp>"
spawn ssh test#test1
expect $shell_prompt
send "sftp test2#test2\r"
expect "*assword:"
send "123456\r"
expect $sftp_prompt
send "get file.xls\r"
expect $sftp_prompt
send "exit\r"
expect $shell_prompt
send "exit\r"
expect eof
EXPECT_SCRIPT
scp test#test1:file.xls .

Resources