spawn_id: spawn id exp6 not open - linux

I know that this issue is already mentioned here, but the solution does not work for me.
I have this script (let's name it myscript.sh) that spawns a process on remote environment and that should interact with it.
#!/usr/bin/expect
log_user 0
set timeout 10
spawn ssh -o PubkeyAuthentication=no [lindex $argv 0] -n [lindex $argv 1]
expect "password:" {send "mypassword\r"}
expect "Continue to run (y/n)" {send "n\r"}
interact
When I call this script on local environment...
myscript.sh user#host "command1;./command2 parameter1 parameter2"
I get the above error at line 7 (interact)
Any ideas??

I suspect the expect is not able to find out(matching) the pattern you are sending.
expect "password:" {send "mypassword\r"}
expect "Continue to run (y/n)" {send "n\r"}
Check out again whether the "password:" and "Continue to run (y/n)" are in correct CAPS.
If still getting the same error, you can try using regular expression.

Try to do a normal ssh without script. See if it works. Sometimes the remote host identification changes, and the host has a new ip or new key. Then it helps to remove the old key with ssh-keygen -f ~/.ssh/known_hosts -R old_host, or something similar.

I had this problem and it was down to using the wrong port.

/usr/bin/expect <<EOF
spawn ssh-copy-id -i $dest_user#$ip
expect {
"yes/no" {
send "yes\r";exp_continue
} "password" {
send "$passwd\r"
} eof {
exit
}
}
expect eof
EOF

I ran into this issue as well but it was due to me creating/editing the following file for an unrelated item:
~/.ssh/config
Once I deleted that, all my scripts began working and I no longer got that issue with my expect file.

Related

how to use expect in linux? [duplicate]

I'm trying to use expect in a Bash script to provide the SSH password. Providing the password works, but I don't end up in the SSH session as I should. It goes back strait to Bash.
My script:
#!/bin/bash
read -s PWD
/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com'
expect "password"
send "$PWD\n"
EOD
echo "you're out"
The output of my script:
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
usr#$myhost.example.com's password: you're out
I would like to have my SSH session and, only when I exit it, to go back to my Bash script.
The reason why I am using Bash before expect is because I have to use a menu. I can choose which unit/device to connect to.
To those who want to reply that I should use SSH keys, please abstain.
Mixing Bash and Expect is not a good way to achieve the desired effect. I'd try to use only Expect:
#!/usr/bin/expect
eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
# Use the correct prompt
set prompt ":|#|\\\$"
interact -o -nobuffer -re $prompt return
send "my_password\r"
interact -o -nobuffer -re $prompt return
send "my_command1\r"
interact -o -nobuffer -re $prompt return
send "my_command2\r"
interact
Sample solution for bash could be:
#!/bin/bash
/usr/bin/expect -c 'expect "\n" { eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com; interact }'
This will wait for Enter and then return to (for a moment) the interactive session.
The easiest way is to use sshpass. This is available in Ubuntu/Debian repositories and you don't have to deal with integrating expect with Bash.
An example:
sshpass -p<password> ssh <arguments>
sshpass -ptest1324 ssh user#192.168.1.200 ls -l /tmp
The above command can be easily integrated with a Bash script.
Note: Please read the Security Considerations section in man sshpass for a full understanding of the security implications.
Add the 'interact' Expect command just before your EOD:
#!/bin/bash
read -s PWD
/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
expect "password"
send -- "$PWD\r"
interact
EOD
echo "you're out"
This should let you interact with the remote machine until you log out. Then you'll be back in Bash.
After looking for an answer for the question for months, I finally find a really best solution: writing a simple script.
#!/usr/bin/expect
set timeout 20
set cmd [lrange $argv 1 end]
set password [lindex $argv 0]
eval spawn $cmd
expect "assword:" # matches both 'Password' and 'password'
send -- "$password\r"; # -- for passwords starting with -, see https://stackoverflow.com/a/21280372/4575793
interact
Put it to /usr/bin/exp, then you can use:
exp <password> ssh <anything>
exp <password> scp <anysrc> <anydst>
Done!
A simple Expect script:
File Remotelogin.exp
#!/usr/bin/expect
set user [lindex $argv 1]
set ip [lindex $argv 0]
set password [lindex $argv 2]
spawn ssh $user#$ip
expect "password"
send "$password\r"
interact
Example:
./Remotelogin.exp <ip> <user name> <password>
Also make sure to use
send -- "$PWD\r"
instead, as passwords starting with a dash (-) will fail otherwise.
The above won't interpret a string starting with a dash as an option to the send command.
Use the helper tool fd0ssh (from hxtools, source for ubuntu, source for openSUSE, not pmt). It works without having to expect a particular prompt from the ssh program.
It is also "much safer than passing the password on the command line as sshpass does" ( - comment by Charles Duffy).
Another way that I found useful to use a small Expect script from a Bash script is as follows.
...
Bash script start
Bash commands
...
expect - <<EOF
spawn your-command-here
expect "some-pattern"
send "some-command"
...
...
EOF
...
More Bash commands
...
This works because ...If the string "-" is supplied as a filename, standard input is read instead...
sshpass is broken if you try to use it inside a Sublime Text build target, inside a Makefile. Instead of sshpass, you can use passh
With sshpass you would do:
sshpass -p pa$$word ssh user#host
With passh you would do:
passh -p pa$$word ssh user#host
Note: Do not forget to use -o StrictHostKeyChecking=no. Otherwise, the connection will hang on the first time you use it. For example:
passh -p pa$$word ssh -o StrictHostKeyChecking=no user#host
References:
Send command for password doesn't work using Expect script in SSH connection
How can I disable strict host key checking in ssh?
How to disable SSH host key checking
scp without known_hosts check
pam_mount and sshfs with password authentication

Use of expect to run scripts on remote machine

I am working on a project that requires some assistance.
I have automated most of the information required for the completion of this project but the only thing that is lagging is the running of local shell scripts on the remote machine.
As we are aware that no Linux command is recognized by the script that uses the 'expect' library.
Herein we have two use cases that I have tried:
1) Running the desired list of commands on the remote server using only one expect script which has both the script execution as well as pushing of output using scp to the local machine, here is a snippet of this code:
`chmod 777 localscript.sh
cat > script1.sh <<- "ALL"`
`#!/usr/bin/expect
set password [lindex $argv 0];
set ipaddress [lindex $argv 1];
set timevalue [lindex $argv 2];
set timeout $timevalue
spawn /usr/bin/ssh username#$ipaddress /bin/bash < ./localscript.sh
expect "assword:"
send "$password\r"
set timeout $timevalue
spawn /usr/bin/scp username#$2:"/path/from/source/*" /path/to/destination/folder/
expect "assword:"
send "$password\r"
interact
ALL
chmod 777 script1.sh
./script1.sh $password $2 $timevalue`
2) Running the desired list of commands on the remote server in a separate expect script and using scp to get files in a different script:
`cat > script1.sh <<- "ALL" `
`#!/usr/bin/expect
set password [lindex $argv 0];
set ipaddress [lindex $argv 1];
set timevalue [lindex $argv 2];
set timeout $timevalue
spawn /usr/bin/ssh username#$ipaddress /bin/bash < ./localscript.sh
expect "assword:"
send "$password\r"
interact
ALL
cat > script2.sh <<- "ALL2"`
`#!/usr/bin/expect
set password [lindex $argv 0];
set ipaddress [lindex $argv 1];
set timevalue [lindex $argv 2];
set timeout $timevalue
spawn /usr/bin/scp username#ipaddress:"/path/from/source/*" /path/to/destination/folder/
expect "assword:"
send "$password\r"
interact
ALL2
chmod 777 localscript.sh script1.sh script2.sh
./script1.sh $password $2 $timevalue
sleep 5
./script2.sh $password $2 $timevalue`
I believe the above codes should both be valid in their own respect however, the output for the same seem to be quite unexpected:
1) Both the commands ssh and scp are being executed almost simultaneously after password is entered hence, it is not giving localscript enough time to do its job, here's the output I see:
spawn /usr/bin/ssh username#1.2.3.4 /bin/bash < ./localscript.sh
Warning private system unauthorized users will be prosecuted.
username#1.2.3.4's password: spawn /usr/bin/scp
username#1.2.3.4:"/home/some/file/*" /another/file/
Warning private system unauthorized users will be prosecuted.
username#1.2.3.4's password:
scp: /home/some/file/*: No such file or directory
Please note: This functionality is working fine without the involvement of expect
2) Here we are executing ssh and scp separately, however, it seems like it is unable to recognize that the file localscript.sh exists:
spawn /usr/bin/ssh username#1.2.3.4 /bin/bash < ./localscript.sh
Warning private system unauthorized users will be prosecuted.
username#1.2.3.4's password:
bash: localscript.sh: No such file or directory
Warning private system unauthorized users will be prosecuted.
username#1.2.3.4's password:
scp: /home/some/file/*: No such file or directory
Any feedback on the same would be appreciated, I think the first approach might be a feasible solution, except the fact that spawn is too fast and none of the 'sleep' or 'after' commands are helping/working. I think the second approach is also valid however it seems like there is a different way of running a local script on a remote server than the usual way we do on Linux when using 'expect'.
Sorry for so much elaboration, I am hoping to be out of my misery soon :)
Indeed the timeout you are setting is not working as you expect it to. Both scripts are spawned, and the expect "assword:" after each spawn is actually catching and reacting to the same password prompt.
expect is actually more sophisticated than a cursory glance would lead you to believe. Each spawn should return a PID, which you can use with your expect to look for output from a specific process.
expect can also be broken down into multiple parts, and have the ability to define subroutines. Here are some more advanced use examples https://wiki.tcl-lang.org/10045
In this specific case I would suggest waiting for the scp to complete before spawning the next process.
expect {
"assword:" {
send "$password\r"
exp_continue # Keep expecting
}
eof {
puts -nonewline "$expect_out(buffer)"
# eof so the process should be done
# It is safe to execute the next spawn
# without exp_continue this expect will
# break and continue to the next line
}
}

What's the best way to mix remote expect scripts and local bash commands?

I'm automating tasks on a local and remote machine (behind a firewall). Once I'm done with tasks on the remote machine, I'd like the script to return to executing commands on the local machine.
#!/usr/bin/expect -f
set timeout -1
spawn ssh username#host
expect "Password: "
send "mypassword\r"
expect "username#host:~$"
...do some stuff...
send "exit\r"
expect eof
[then, once on the local machine, change directories and do other things]
What's the best way to append bash commands? I suppose I could start with bash, call expect within it, then simply return to bash once expect is done.
Expect is based on Tcl, so it can run the same commands. But if your goal is to run bash commands, the best bet is to run them from bash as a separate script, exactly as you propose in your last sentence.
It really depends on what your idea of ...do some stuff... is. Here's an example of something I recently did from my OSX w/s to an AWS instance
export all_status
init_scripts=($(ssh -q me#somehost 'ls /etc/init.d'))
for this_init in ${init_scripts[#]};do
all_status="${all_status}"$'\n\n'"${this_init}"$'\n'"$(ssh -q somehost \'sudo /etc/init.d/${this_init} status\')"
done
echo "$all_status" > ~/somehost_StatusReport.txt
unset all_status
Passing a command at the end of the ssh command will cause the command to be run on the remote host. Or you can scp a script to the remote host and run it with
ssh somehost '/home/me/myscript'
I met this situation recently too. I make a shell supexpect.sh which could login and execute command automatically. It will return to your local shell at the end.
#!/usr/bin/expect
#Usage:supexpect <host ip> <ssh username> <ssh password> <commands>
set timeout 60
spawn ssh [lindex $argv 1]#[lindex $argv 0] [lindex $argv 3]
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "[lindex $argv 2]\r" }
} "*?assword" { send "[lindex $argv 2]\r" }
send "exit\r"
expect eof
To execute:
./supexpect.sh 10.89.114.132 username password "ls -a;pwd;your_stuff_on_remote_host"
Note:
The prompt might need to adapt to your own system, and of course you need to pass execute permission to it.

When I try to use linux expect to ssh remote machine, the authentication is very slow ,why?

I try to use it , but I find It is very slow .
my code:
#!/usr/bin/expect -f
set hostname [lindex $argv 0]
spawn ssh "xxx\#$hostname"
expect "password: "
send "xxxxx\r"
expect "$ "
send "sudo su admin\r"
expect "assword:\n"
send "xxxxx\r"
expect "$ "
send "cat /tmp/id_rsa.pub >> /home/admin/.ssh/authorized_keys\r"
expect "$ "
send "exit\r"
Do you have any suggestion?
Add exp_internal 1 to the top of your script so you can watch what expect is doing.
I suspect this is wrong: expect "assword:\n" -- I bet the actual password prompt does not end with a newline. Try removing \n
Try
ssh -o GSSAPIAuthentication=no login#remoteipipaddress
if successful.. need to edit /etc/ssh/ssh_config and /etc/ssh/sshd_config
and looking for GSSAPIAuthentication. Please change value to "no".
it's working for me.

Modify expect-based SSH script to work on machines that don't require a password

The following expect script works fine when the Linux machine asks for a password after login. But some of our Linux machines don't need a password for SSH (we can login without a password), so I need to change the expect script in order to support machines without a password. How can I do that?
$ expect_test=`cat << EOF
set timeout -1
spawn ssh $IP hostname
expect {
")?" { send "yes\r" ; exp_continue }
word: {send "pass123\r" }
}
expect eof
EOF`
$ expect -c "$expect_test"
When running on a machine that needs a password:
$ IP=10.17.18.6
$ expect -c "$expect_test"
spawn ssh 10.17.18.6 hostname
sh: /usr/local/bin/stty: not found
This computer system, including all related equipment, networks and network devices (specifically including Internet access),is pros
yes
Password:
Linux1_machine
When running on a machine that doesn't need a password:
$ IP=10.10.92.26
$ expect -c "$expect_test"
spawn ssh 10.10.92.26 hostname
sh: /usr/local/bin/stty: not found
Linux15_machine
expect: spawn id exp5 not open
while executing
"expect eof"
Use this expect command:
expect {
")?" {send "yes\r"; exp_continue}
word: {send "pass123\r"; exp_continue}
eof
}
That way, if EOF is encountered before "password:", the script will act normally.
Change you timeout from -1 to something else, this will cause expect to move on to the next line if the expected string does not show up within the given timeout.
The current value, -1 causes it to block forever if not password is prompted for.
UPDATE:
set timeout 5
spawn ssh $IP hostname
expect {
")?" { send "yes\r" ; exp_continue }
word: {send "pass123\r" }
eof {exit}
}

Resources