SSH/Expect How does "expect" work in expect shell script? - macos

I try to use ssh remote login with expect. It works, but some outputs are not expected and I do not know why. This is my shell script:
#!/bin/sh
expect -c ' spawn ssh USER#ADDRESS ;
expect "?assword:" ;
send "MyPassword\r" ;
expect "?" ;
send "logout\r" ;
interact'
This is the output in my MacOS terminal:
ip87-114:Downloads tasiyuchien$ ./test.sh
spawn ssh USER#ADDRESS
Password:
logout
Last login: Tue Sep 13 18:17:21 2022 from ADDRESS
xdn42o221:~ USER$ logout
Connection to ADDRESS closed.
ip87-114:Downloads tasiyuchien$
The first question is why there is a "logout" output after "Password:"? Isn't the question mark represents any single character? Is there any implicit output or pattern I don't see cause I see nothing after "Password:". (Also strangely, when I replace "?" as "*", nothing will be output after "Password:" and the automatic logout also failed.) The same question can be asked when I login, cause I also see nothing but the "logout" is output.
The second question is why are there two "logout" outputted, I thought the except argument can be reused only if "except continue" is added.
Can anyone explain to me what's happening. Thanks!

As part of the login process, after the Password: prompt is shown, you enter your password and hit Enter, then the login process prints a newline. This newline is the character matched by "?".
Changing "?" to "*" works better: the asterisk is more greedy probably. Instead of matching exactly one character, it matches one or more characters, so it will try to collect as many as possible.
Why two "logout"s appear? I don't know. Perhaps your shell re-prints user input after displaying your prompt..

Related

capture expect ssh output to variable

Hey am new to bash scripts and was wondering how would I capture the output of the ssh command into a bash variable? I looked around and cant seem to get it right. I have tried puts $expect_out(buffer) but when echo it says variable does not exist
I know the response should be just one line and if I want to save that into a variable response and then echo it how would I do that?
A generic idea can be something like as below.
spawn the ssh session
make proper login
Send each commands with send
Wait for desired output with expect
Example:
spawn ssh $user#$domain
expect "password" { send "$pwd\r"}
expect "#"; # This '#' is nothing but the terminal prompt
send "$cmd\r"
expect "#"
puts $expect_out(buffer); #Will print the output of the 'cmd' output now.
The word to wait for after executing the command can vary based on your system. It can be # or $ or > or :; So, make sure you are giving the correct one. Or, you can provide a generalized pattern for the prompt as such
set prompt "#|>|:|\\\$"; # We escaped the `$` symbol with backslash to match literal '$'
While using the expect after sending the commands, it can be used as
expect -re $prompt; #Using regex to match the pattern`

BASH scripting for username/password constructs

I want to write a simple bash script using ncat to open a connection to a ISP and its port.
The first command would be:
nc address port
Upon doing this, I am prompted first to provide a username. I must hit ENTER, and then I will be prompted to provide a password and then I must hit ENTER again.
After this, I want to open a Terminal process window. Can anyone point me to sufficient resources for this type of scripting?
I know the username and password already, but I'm not too sure how to work around the fact that I must provide it and then hit enter. I'm also unsure how to open a new Terminal proceses.
Thanks in advance!
Check out expect script
Expect
Example:
# Assume $remote_server, $my_user_id, $my_password, and $my_command were read in earlier
# in the script.
# Open a telnet session to a remote server, and wait for a username prompt.
spawn telnet $remote_server
expect "username:"
# Send the username, and then wait for a password prompt.
send "$my_user_id\r"
expect "password:"
# Send the password, and then wait for a shell prompt.
send "$my_password\r"
expect "%"
# Send the prebuilt command, and then wait for another shell prompt.
send "$my_command\r"
expect "%"
# Capture the results of the command into a variable. This can be displayed, or written to disk.
set results $expect_out(buffer)
# Exit the telnet session, and wait for a special end-of-file character.
send "exit\r"
expect eof
The secret lies in the HEREDOC
You can solve this problem with something akin to:
$ command-that-needs-input <<EOF
authenticate here
issue a command
issue another command
EOF
Look at the link I provided for here documents - it includes support for variable substitution and lots of other useful things. Enjoy!

Passwordless Login

Problem statement
I want to access a server without asking me the password (will be mentioned in the script) and run a command on that server.
My Code
#!/usr/bin/expect
spawn sudo su - <server_name>
expect "[sudo] password for chronicles:"
set Password "xxxxxxx"
send "$Password\r"
#set timeout 300
send "whoami\r"
send "ls -ltr\r"
expect eof
Output
invalid command name "sudo"
while executing
Restrictions
I dont have access rights to change env variables or modify
.bash_profile / .bashrc.
su server_name command not allowed
David is right that generally this is a bad idea. There are occasionally good reasons for doing it, or doing something similar (e.g. automatically logging into serial consoles for lights-out management), but you haven't provided any indication as to why it makes sense for you to do it this way.
Caveats aside, the invalid command name is not coming from the spawn line but from the [sudo] in the expect line. Expect is based on tcl, which treats [] square parentheses as special characters indicating command substitution. Additionally, the value passed to expect is a glob pattern not a fixed string, and [] square parentheses are also special characters in globs. So the answer you are looking for is to quote those characters twice:
expect "\\\[sudo\\\] password for chronicles:"
Also note that after sending the password you should probably include another expect line to wait for the root shell prompt.
The secure way to access a server without prompting for a password is through keyed logins over SSH. Don't ever give your password in plain text.
If you simply Google, you will find many articles explaining how to do this. SSH login without password is a perfectly fine explanation.
[] is interpreted as "command quotes" ("command" as in "Tool Command Language", which is what Tcl is short for) in Tcl.
{} is the strongest quote in Tcl, you can use it to prevent any interpretation:
expect {[sudo] password for chronicles:}
of course you could also just omit [sudo]:
expect "password for chronicles:"

How to make expect command in expect program script to wait for exact string matching

I want to know how to make expect command in expect script to wait for exact string to be matched before proceeding to next line in the script.
Bourne shell (sh) script:
#!/bin/sh
#!/usr/bin/expect
spawn ssh -Y localuser#lbblr-tirumala
expect -exact "localuser#lbblr-tirumala's password: "
# replace password with ur own passwd
send "geomax45\r"
# replace the prompt with ur own prompt
expect "localuser#lbblr-tirumala:~$ "
# run application
send "./sample\r"
expect "*Menu*\r
1. print hello world\r
2. print Bye world\r
3. quit\r
enter choice: "
# enter choice 1
send "1\r"
expect "Bye world\r
*Menu*\r
1. print hello world\r
2. print Bye world\r
3. quit\r
enter choice: $"
# enter choice 2
send "2\r"
In the above code after entering 1 the code should wait for "Bye world\r......" string to occur on the screen. But even though the screen prompts different string altogether, the code is executing the next line and entering option 2, which should not occur as per definition of expect. It should wait until the complete string in expect command matches.
I want to know whether there is any stricter command that can be used so that expect waits until exact string matching occurs. Please help me in this issue
You're already using it earlier in your script: expect -exact. But long matching strings are pretty much guaranteed to cause problems; you should try to cut it down to the shortest unique match, if necessary breaking it into multiple expect commands.
In this case, just based on what you show, the proper command is probably expect -exact "choice:". But inherently it is not possible to determine this without full details of the possible program outputs.

SSH login with expect(1). How to exit expect and remain in SSH? [duplicate]

This question already has answers here:
Using expect to pass a password to ssh
(6 answers)
Closed 5 years ago.
So I wanted to automate my SSH logins. The host I'm with doesn't allow key authentication on this server, so I had to be more inventive.
I don't know much about shell scripting, but some research showed me the command 'expect' and some scripts using it for exactly this purpose. I set up a script and ran it, it worked perfectly to login.
#!/usr/bin/env expect -f
set password "my_password"
match_max 1000
spawn ssh -p 2222 "my_username"#11.22.11.22
expect "*?assword:*"
send -- "$password\r"
send -- "\r"
expect eof
Initially, it runs as it should.
Last login: Wed May 12 21:07:52 on ttys002
esther:~ user$ expect expect-test.exp
spawn ssh -p 2222 my_username#11.22.11.22
my_username#11.22.11.22's password:
Last login: Wed May 12 15:44:43 2010 from 20.10.20.10
-jailshell-3.2$
But that's where the success ends.
Commands do not work, but hitting enter just makes a new line.
Arrow keys and other non-alphanumeric keys produce symbols like '^[[C', '^[[A', '^[OQ' etc.[1]
No other prompt appears except the two initially created by the expect script.
Any ignored commands will be executed by my local shell once expect times out.
An example:
-jailshell-3.2$ whoami
ls
pwd
hostname
(...time passes, expect times out...)
esther:~ user$ whoami
user
esther:~ ciaran$ ls
Books Documents Movies Public
Code Downloads Music Sites
Desktop Library Pictures expect-test.exp
esther:~ ciaran$ pwd
/Users/ciaran
esther:~ ciaran$ hostname
esther.local
As I said, I have no shell scripting experience, but I think it's being caused because I'm still "inside of" expect, but not "inside of" SSH. Is there any way to terminate expect once I've logged in, and have it hand over the SSH session to me?
I've tried commands like 'close' and 'exit', after " send -- "\r" ". Yeah, they do what I want and expect dies, but it vindictively takes the SSH session down with it, leaving me back where I started. What I really need is for expect to do its job and terminate, leaving the SSH session back in my hands as if I did it manually.
All help is appreciated, thanks.
[1] I know there's a name for this, but I don't know what it is. And this is one of those frightening things which can't be googled, because the punctuation characters are ignored. As a side question, what's the story here?
I think your problem has been solved here before:
Using expect to pass a password to ssh
The command you're looking for is interact. It hands the control over to you/your keyboard.
I've used a similar script to autologin.
I used "interact" and I removed "expect eof". By doing this, I can get the screen back so that I can enter commands by hand.
expect "?assword: "
send -- "$password\r"
expect "$"
interact
putting it all together, log you in and leave you on the command line exactly as though you typed it manually
#!/usr/bin/expect -f
set ip "127.001.001.001"
set password "xxyykkx"
spawn ssh $ip -l root
expect "?assword:"
send "$password\r"
interact

Resources