How to pass a value from bash into expect? - linux

I've got a really simple bash script which requires expect.
I need to pass a value from bash into expect and I'm not trying to ssh into another server or anything (cause I only seem to find questions regarding logging into another server via ssh).
The idea is simply something like this:
#!/usr/bin/env bash
echo "Please enter your password: "
read PASSWD
x=$(expect -c '
spawn su -c 'whoami'
expect "Password:"
send "$PASSWD\r"
interact
')
So this doesn't work. The expect shell doesn't recognize the $PASSWD variable.
How may this be accomplished?
Thank you.

Another option would be to store the PASSWD in the environment and let expect pick it up there:
read -p "Your password: " passwd
export passwd
expect -c '... ; send "$env(passwd)\r"; ...'
Probably the best choice security-wise is have expect prompt for the password: then, the password will not show up on the command line nor in the environment.
expect -c <<'END'
stty -echo
send_user "Your password: "
expect_user -re "(.*)\n"
send_user "\n"
set passwd $expect_out(1,string)
stty echo
# your script starts here
...
send "$passwd\r"
...
END

Variables within single quotes are not expanded by the shell, that's why in this case $PASSWD remains the literal string $PASSWD.
Try changing the quotes:
#!/usr/bin/env bash
echo "Please enter your password: "
read PASSWD
x=$(expect -c "
spawn su -c 'whoami'
expect 'Password:'
send '$PASSWD\r'
interact
")
One more note: you should be aware that this could pose a security risk, as the password will be visible in plaintext in the process list while the command is running.

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

Can not enter password in Shell Script

I can not enter my pass word.
My shell script is here.
#!/bin/bash
expect -c "
set timeout 2
spawn ftp ${HOST}
expect \"Name\(${HOST}:root\):\"
send ${USER}\n
expect \"Password:\"
send ${PASS}\n
interact
"
I can enter user-name, but password is not work....
like this return.
spawn ftp ******
Connected to ****** (******).
220 (vsFTPd 2.2.2)
Name (******:root): ${USER}
331 Please specify the password.
Password:[root#........ bin]#
I think I send password... Could you please help me?
Thank you.
SOLVE MY QUESTION
I think it is easy for me to use this.
it is easy for me to use this.
#!/bin/sh
SERVER=$1
USER=$2
PASS=$3
FILE=$4
ftp -n <<END
open $SERVER
user $USER $PASS
cd /tmp
binary
prompt
mput $FILE
END
you have to prompt password and send it to variable
echo -n "Enter Password:"
read PASS

Using 'expect' command to pass password to SSH running script remotely

I need to create a bash script that will remotely run another script on a batch of machines. To do so I am passing a script through SSH.
ssh -p$port root#$ip 'bash -s' < /path/to/script/test.sh
I thought it would use my RSA keys but I am getting error:
"Enter password: ERROR 1045 (28000): Access denied for user 'root'#'localhost' (using password: YES)"
I tried using sshpass to no avail. So my next solution was using expect. I have never used expect before and I'm positive my syntax is way off.
ssh -p$port root#$ip 'bash -s' < /path/to/script/test.sh
/usr/bin/expect <<EOD
expect "password"
send "$spass\n"
send "\n"
EOD
I have root access to all machines and ANY solution will do as long as the code remains within bash. Just keep in mind that this will be done in a loop with global variables ($spass, $ip, $port, etc) passed from a parent script.
You are doing it wrong in two means:
If you want expect to interact with ssh, you need to start ssh from expect script and not before.
If you put the script (/path/to/script/test.sh) to stdin of ssh, you can't communicate with the ssh process any more.
You should rather copy the script to remote host using scp and then run it.
Expect script might look like this:
/usr/bin/expect <<EOF
spawn ssh -p$port root#$ip
expect "password"
send "$Spass\r"
expect "$ "
send "/path/to/script/on/remote/server/test.sh\r"
expect "$ "
interact
EOF
#!/usr/bin/expect
#Replace with remote username and remote ipaddress
spawn /usr/bin/ssh -o StrictHostKeyChecking=no username#IPAddress
#Replace with remote username and remote ipaddress
expect "username#IPAddress's password: "
#Provide remote system password
send "urpassword\n"
#add commands to be executed. Also possible to execute bash scripts
expect "$ " {send "pwd\n"} # bash command
expect "$ " {send "cd mytest\n"}
expect "$ " {send "./first.sh\n"} # bash scripts
expect "$ " {send "exit\n"}
interact

expect, interact and then again expect

There are several posts regarding the same, but i still not able to make my expect script work properly. My intention is to automate everything but leave the password enter for the user. So there are 3 parts of the script:
automated login
give the user interaction to enter the password
give control back to Expect script to continue work
So i have script which will be spawned and which have 3 read commands. First and last should be filled by Expect and second one i would like to enter my self:
#!/bin/ksh
read user?User:
echo "Expect entered the username $user"
read pass?Password:
echo "User entered the password $pass"
read command?"Shell>"
echo "Expect entered the command $command"
My expect script:
#!/usr/bin/expect
spawn ./some_script
expect User
send I-am-expect\r
expect Password
interact
expect Shell
send I-am-expect-again
Unfortunately after i have entered the password the script does not continue and left in the interact mode:
[root#localhost ~]# ./my-expect
spawn ./some_script
User:I-am-expect
Expect entered the username I-am-expect
Password:i am user
User entered the password i am user
Shell>
And finally when i entering something on the "Shell" and pressing [ENTER] expect exits with the error:
Expect entered the command
expect: spawn id exp4 not open
while executing
"expect Shell"
(file "./my-expect" line 7)
[root#localhost ~]#
I appriciate any explanation or resolution of this issue. I am using expect version 5.45
You can read (expect_user) the user's password by yourself and then send it to the spawn'ed program. For example:
[STEP 101] # cat foo.exp
proc expect_prompt {} \
{
global spawn_id
expect -re {bash-[.0-9]+(#|\$)}
}
spawn ssh -t 127.0.0.1 bash --noprofile --norc
expect "password: "
stty -echo
expect_user -timeout 3600 -re "(.*)\[\r\n]"
stty echo
send "$expect_out(1,string)\r"
expect_prompt
send "exit\r"
expect eof
[STEP 102] # expect foo.exp
spawn ssh -t 127.0.0.1 bash --noprofile --norc
root#127.0.0.1's password:
bash-4.3# exit
exit
Connection to 127.0.0.1 closed.
[STEP 103] #
The interact should be given with proper condition for the exit criteria.
The following script will execute the user commands in the shell
exeCmds.sh
#!/bin/bash
read -p "User: " user
echo "Expect entered the username $user"
read -p "Password: " pass
echo "User entered the password $pass"
while :
do
# Simply executing the user inputs in the shell
read -p "Shell> " command
$command
done
automateCmdsExec.exp
#!/usr/bin/expect
spawn ./exeCmds.sh
expect User
send dinesh\r
expect Password
send welcome!2E\r
expect Shell>
puts "\nUser can interact now..."
puts -nonewline "Type 'proceed' for the script to take over\nShell> "
while 1 {
interact "proceed" {puts "User interaction completed.";break}
}
puts "Script take over the control now.."
# Now, sending 'whoami' command from script to shell
send "whoami\r"
expect Shell>
# Your further code here...
The script automateCmdsExec.exp will address the login needs of the bash script and when the prompt arrives, it will hand over the control to user.
We should define an exit criteria for the interact for which I have used the word proceed. (You can alter it as per your need).
Once interact matched the word proceed. it will return the control back to the expect script.
For demo purpose, I kept one more send-expect pair of command.
i.e.
send "whoami\r"
expect Shell>
You can keep your further code below the interact, thus it can be executed by script.

Get one line of user input and then execute it as Bash commands

I have wrote a expect script that helps to execute commands in remote machine. When the execution is completed, I want to get one line of user input and then send it to the remote bash, here is the code snippet:
#! /usr/bin/env expect
...
spawn ssh -l $user $host
...
send_tty -- "Enter your command: "
set timeout -1
# match only printable characters (prevent from pressing TAB)
expect_tty eof exit -re {([[:print:]]*)\n}
send_tty -- "\n"
set timeout 10
# send the command to remote shell
send "$expect_out(1,string)"
expect "$GENERAL_PROMPT"
However, if the input is something like: ls /", my program will be blocked because the remote shell expects to get more characters by prompting the string "> ". Actually, I hope bash won't prompt for more input instead of just printing error message:
$ read COMMAND
ls /"
$ eval "$COMMAND"
bash: unexpected EOF while looking for matching `"'
bash: syntax error: unexpected end of file
Can I achieve this in my script?
#!/usr/bin/expect
set prompt "#|%|>|\\\$ $"; # A generalized prompt to match known prompts.
spawn ssh -l dinesh xxx.xx.xx.xxx
expect {
"(yes/no)" { send "yes\r";exp_continue}
"password"
}
send "mypassword\r"
expect -re $prompt
send_tty -- "Enter your command: "
set timeout -1
# match only printable characters (prevent from pressing TAB)
expect_tty eof exit -re {([[:print:]]*)\n}
send_tty -- "\n"
set timeout 10
puts "\nUSER INPUT : $expect_out(1,string)"
# send the command to remote shell
# Using 'here-doc', to handle possible user inputs, instead of quoting it with any other symbol like single quotes or backticks
send "read COMMAND <<END\r"
expect -re $prompt
send "$expect_out(1,string)\r"
expect -re $prompt
send "END\r"
expect -re $prompt
# Since we want to send the literal dollar sign, I am sending it within braces
send {eval $COMMAND}
# Now sending 'Return' key
send "\r"
expect -re $prompt
Why 'here-doc' used ?
If I have used backticks or single quotes to escape the commands, then if user gave backticks or single quotes in the commands itself, then it may fail. So, to overcome that only, I have added here-doc.
Output :
dinesh#MyPC:~/stackoverflow$ ./zhujs
spawn ssh -l dinesh xxx.xx.xx.xxx
dinesh#xxx.xx.xx.xxx's password:
[dinesh#lab ~]$ matched_literal_dollar_sign
Enter your command: ls /"
USER INPUT : ls /"
read COMMAND <<END
> ls /"
> END
[dinesh#lab ~]$ eval $COMMAND
-bash: unexpected EOF while looking for matching `"'
-bash: syntax error: unexpected end of file
[dinesh#lab ~]$ dinesh#MyPC:~/stackoverflow$
Update :
The main reason for using here-doc is due to the fact that it makes the read to act as non-blocking command. i.e. We can proceed quickly with next command. Else, we have to wait till the timeout of Expect. (Of course, we could change the timeout value dynamically.)
This is just one way of doing it. You can alter it if you want, with simply having the read command.
I think this would be a good case for interact -- get expect to step aside and let the user interact directly with the spawned program.
spawn ssh -l $user $host
#...
send_user "You are now about to take control: type QQQ to return control to the program\n"
interact {
QQQ return
}
send_user "Thanks, I'm back in charge ...\n"
This is a one line version
read > export cmd ; eval $cmd ; unset cmd

Resources