I want to login from 192.168.119.128 to 192.168.119.129 automatic and run some commands, so I write an expect script.
a.sh
#!/usr/bin/expect -f
set timeout 5
spawn ssh root#192.168.119.129
expect "password" {send "123456\r"}
expect "]#" {send "touch /tmp/a.txt\r"}
#interact
The output is:
kaiwen#kaiwen-virtual-machine:~/Work$ ./a.sh
spawn ssh root#192.168.119.129
root#192.168.119.129's password:
Last login: Sun Jan 22 17:36:21 2017 from 192.168.119.128
[root#localhost ~]# kaiwen#kaiwen-virtual-machine:~/Work$
I login successfuly, but it seems touch /tmp/a.txt command is not run.
When I uncomment the last line #interact of a.sh, it works, and the file a.txt is created.
#!/usr/bin/expect -f
set timeout 5
spawn ssh root#192.168.119.129
expect "password" {send "123456\r"}
expect "]#" {send "touch /tmp/a.txt\r"}
interact
Here is the output:
kaiwen#kaiwen-virtual-machine:~/Work$ ./a.sh
spawn ssh root#192.168.119.129
root#192.168.119.129's password:
Last login: Sun Jan 22 17:41:23 2017 from 192.168.119.128
[root#localhost ~]# touch /tmp/a.txt
[root#localhost ~]#
Why without the interact directive the script work incorrect? Thanks.
Without interact the Expect script will exit after the last command expect "]#" and it'll kill the spawned process. It's just like you close the SSH client application (like PuTTY) window when the shell is still alive running.
The interact is a long-running command which waits for the spawned process to exit.
Related
I am currently trying to create a script with error handling.
Basically the script tests the ssh connection with this command :
ssh -o BatchMode=yes $machine uname -a
There is 3 potential situation that i want to handle :
SSH works just fine without password
SSH is blocked because the machine isn't in the known_hosts file in .ssh
SSH is blocked because the machine isn't in the known_hosts file in .ssh AND it requires a password to continue (which means the id_rsa.pub isn't in the authorized_keys file in .ssh
I have on main script that is calling an expect script here is what the main script looks like :
ssh -o BatchMode=yes ${machine} uname -a &> temp-file.txt 2>&1
# Here we test the ssh connection just once and we store the output of the command in a temp file
if [ $? -eq 255 ]
# If the ssh didn't work
then
if grep -q "Host key verification failed." temp-file.txt
# If the error message is "Host key verification failed."
then
expect script-expect-knownhosts.exp ${machine} 2>&1 >/dev/null
And here is the script-expect-knownhosts.exp file in which i tried to make a condition :
#!/usr/bin/expect -f
set machine [lindex $argv 0]
# Here we state that the first argument used with the command will be the $machine variable
set prompt "#|%|>|\$ $"
set timeout 60
spawn ssh $machine
# We do a ssh on the machine
set prompt "#|%|>|\$ $"
expect {
"Are you sure you want to continue connecting (yes/no)? " {send "yes\r";exp_continue}
# If he asks for a yes/no answer, then answer yes to add the machine to the known_hosts file
-exact "Password: " {send -- "^C";exp_continue}
# If he asks for a password, then send a CTRL + C
-re $prompt {send "exit\r";exp_continue}
# If the prompt shows up (if after the yes/no question, we don't need to put a password in) then type exit
}
So here is what happens when i execute the expect script with a machine in case number 2 (works just fine):
spawn ssh machine
Are you sure you want to continue connecting (yes/no)? yes
machine:~ # exit
deconnection
Connection to machine closed.
And here is what happens when i execute the expect script with a machine in case number 3 :
spawn ssh machine
Are you sure you want to continue connecting (yes/no)? yes
Password:
And it stays stuck on Password until i manually do a CTRL + C
In cases 2 and 3 you don't need exp_continue because you are stopping the connection process.
For case 2, I don't think you really want to send a control-C. When you do this interactively, typing control-C has the effect of sending a signal to kill the process you are interacting with. What you really want is to stop the ssh process, so instead of send -- "^C";exp_continue you should just do close.
I am trying to execute commands on a remote UNIX host using send and expect ssh module, but even if the script logs in to the server successfully it does not execute commands.
#!/usr/bin/expect
set timeout 60
spawn ssh xxxx#xxxxxx
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "xxxxxx\r" }
} "*?assword" { send "xxxxxxx\r" }
expect "$ "
#sleep 5
send "ps -aef \r"
Output
[xxxxx#xxxxxx Scripts]$ ./TestExpect.sh
spawn ssh xxxxx#xxxxxx
xxxxxx#xxxxxx's password:
Last login: Wed May 9 02:05:47 2018 from xxxxxxxxx
Kickstarted on 2015-05-12
[xxxxx#xxxxx ~]$ [xxxxxx#xxxxx Scripts]$
The Prompt looks like below
[aacdd123#linprod345 ~]$
Issue may be because, you are not expecting anything after sending the ps -aef. Hence the expect spawn process has exited before printing the output.
Try adding few more commands after the sending ps -aef
send "ps -aef\r"
expect $prompt
send "echo hello\r"
expect $prompt
Try looking into the expect_out buffers too, which will give you the captured streams.
puts $expect_out(buffer)
I have this expect script
#!/usr/bin/expect -f
set pass [ exec echo my_phrase | gpg --batch --quiet --yes --passphrase-fd 0 -d /root/.password-store/ssh/my_pgp_store.gpg ]
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no -i /home/myuser/.ssh/id_rsa ssh-user#remote-host.com /some/remote/script > /home/myuser/ssh.output
expect "*?assword:*"
send -- "$pass\r"
interact
that is supposed to read a gpg encrypted password, and use it to call a remote script via ssh (there are many reasons why I need to do it like that). Now, my problem is that whenever I run this script, the ssh connection is correctly set up, but it fails in writing the output of the ssh call with the error
bash: /home/myuser/ssh.output: Permission denied
I'm running the script as root (because I have to run it as root), and I already tried to change the permissions and ownerships of all the involved files and directories.
The redir char > is not special for expect's spawn command. The command
spawn ssh user#host command > outfile
means the same as
spawn ssh user#host "command > outfile"
and the redir > outfile will be executed on the remote host.
Try like this:
spawn bash -c "ssh user#host command > outfile"
expect "assword:"
send -- "$pass\r"
interact
I am creating a script that is executed from host-A to connect to a remote host-b, runs a bash script (which generates monitoring data from top etc.) on host-b, and appends the output back to a file on host-A. While I use ssh to connect, I will not be using ssh-keys as there are simply way too many servers in the environment.
My expect script:
"#!/usr/bin/expect
"#!/usr/bash
set timeout 10
set hostname [sample_hostname]
set user "(sample_user)"
set password "(sample_password)"
spawn ssh $user#$hostname /path/to/bash/script/script.sh | cat >> /tmp/file
expect "Password:"
send "$password\r";
expect "$"
====================================
The above script appends the output of the bash script into host-B /tmp/file instead of the desired host-A /tmp/file. How can I change to have script append to host-A instead of host-b?
P.S.
manually logging into host-A
cd to script location
run ssh user#hostname /path/to/script/script.sh | cat >> /tmp/file on host-A
cat /tmp/file
Above manual steps performs as desired; hence my idea that problem is with bash in expect.
Any ideas are appreciated!
Regards,
Alan
If your intention is to get just the output of that script, then proceed with the following way.
#!/usr/bin/expect
set timeout 10
set hostname host
set user dinesh
set password pwd
set script "/home/dinesh/mypgms/demo.sh"
spawn ssh $user#$hostname
expect "password:"
send "$password\r";
expect "\\\$"; # Matching the literal dollar prompt sign of terminal
send "$script\r"
expect -re "\r\n(.*)\\\$"; # Matching the output
# Getting first sub-match as result
puts "\nResult : --->$expect_out(1,string)"
This will give the output as
dinesh#dinesh-VirtualBox:~/stackoverflow$ ./alan
spawn ssh dinesh#host
dinesh#host's password:
Last login: Tue Apr 21 13:30:35 2015
[dinesh# ~]$ /home/dsivaji/mypgms/demo.sh
{\"INFO\":0}\0\r
{\"INFO\":0}\0\r
[dinesh#~]$
Result : --->{\"INFO\":0}\0\r
{\"INFO\":0}\0\r
[dinesh#~]
dinesh#dinesh-VirtualBox:~/stackoverflow$
Note that the result as
{\"INFO\":0}\0\r
{\"INFO\":0}\0\r
[dinesh#~]
Since we are matching upto the prompt $, it contains till that point. You can customize the regular expression based on the shell output also.
I'm assuming you are using the expect/set method beacuse of the password.
I found another application called sshpass once installed you can just run the command like this inside your bash.
sshpass -p PASSWORDHERE ssh user#hostname /path/to/bash/script/script.sh | cat >> /tmp/file
http://linux.die.net/man/1/sshpass
enjoy
the shell code:
~ cat test_longin.sh
#!/usr/bin/expect
set timeout 120
spawn /usr/bin/ssh fuyou#target.server
expect "*assword*"
send "fuyou\n"
interact
When I type ./test_login.sh, it can login target servers successfully, but when I execute ls command, the console doesn't output any text. So I can't exec any commond on target server. Is the interaction problematic?
I think you should expect something after send, but not use command "interact" immediately.
xxx:/home/username/temp # cat t1.exp
#!/usr/bin/expect
spawn ssh serverx
expect "#"
send "ls -l\r"
expect "#"
interact