Several problems:
to read a line, it's IFS= read -r line
. Your read -s -p "Enter the password : " passd
in particular won't work properly for users whose password contains backslashes or start or end in characters of $IFS
. So it should be IFS= read -rsp 'Enter the password: ' passd || exit
in bash, parameter expansions in list contexts must be quoted. "$user@$1"
, not $user@$1
for instance.
that -o StrictHostKeyChecking=no
sounds a bit like please hack me. Unless the network can be fully trusted, authenticating the server to the client is almost as important as authenticating the client to the server. And that directive would be like saying "trust the user to be whoever they say they are without asking them for proof of identity" if it were done on the server side.
passwords and other secret information should never be passed in command arguments which are public information within a system. The sshpass
man page has a big warning about that. Use SSHPASS="$passd" sshpass -e ssh...
, or better still use better forms of authentication than password authentication.
It's good practice to unset
variables before having them store secret data to make sure they're not exported to the environment (and leak to commands that might themselves leak them further afield; likely not the case here). In bash, that's with unset -v passd
.
With sshpass ... ssh ... << 'HEREDOC'
, the here-document becomes the stdin of sshpass
which passes it along to ssh
which passes it along to the remote command. Here, you're not specifying which command to run, so it will be the login shell of the remote user, whichever it it.
Since ssh
's stdin is not a terminal device, it will request sshd
not to create a pseudo-terminal on the remote side, even with -t
which is just as well, so that shell will be started
non-interactively which again is just as well as it would otherwise start
echoing a prompt and do all the things an interactive shell does while here you want to to execute a script.
So here, that shell will read the code to execute from its stdin.
But that code also happens to read from its stdin. That read -p "Enter your choice [ 1 - 3 ]" choice
, would read from that heredoc. Thankfully, that read
won't be run until the code for the full while
loop has been read by the shell, so it will only read what's after that in that here-doc, or if there's nothing after that, it will just read nothing and return failure upon eof.
Here, you need to run a bash
shell (since you're using bash-specific syntax) on the remote host, not the user's login shell (who would use bash as their login shell? :-b), non-interactively and pass it the code some other way than via stdin. If the ssh server has some AcceptEnv LC_*
in its configuration which is common, you could do:
code=$(
cat <<'EOF'
the code there
EOF
)
And then:
SSHPASS="$passd" LC_CODE="$code" sshpass -e ssh -o SendEnv=LC_CODE -qt "$user@$1" '
exec bash -c -- "$LC_CODE"'
Here assuming the login shell of $user
on the remote host understands that syntax, that is where $var
is used to refer to variables and double-quotes can be used for quoting (and which are necessary on Bourne-like ones to avoid the split+glob). If their login shell is rc
or derivative or zsh
or fish
, exec bash -c -- $LC_CODE
is enough (though the quotes would only be harmful for rc
-like shells); if it's csh
or tcsh
: exec bash -c -- $LC_CODE:q
.
Writing it:
SSHPASS="$passd" LC_CODE="$code" sshpass -e ssh -o SendEnv=LC_CODE -qt "$user@$1" '
exec bash -c '\''eval -- "$LC_CODE"'\'
Would work if the user's login shell is any of the ones above, as the bash -c 'eval -- "$LC_CODE"'
code is understood the same by all of Bourne-like, rc-like, csh-like and fish shells at least. Also has the advantage of not exposing that code in the output of ps -Awww
on the remote host.
You can learn more about that at: How to execute an arbitrary simple command over ssh without knowing the login shell of the remote user?
SSHPASS="$passd" sshpass -e ssh -qt "$user@$1" " $code"
As in @aviro's answer (with the addition of that extra space before $code
, needed for the cases where $code
starts with -
or +
¹) would only work if the login shell of the user happened to be bash
. Also note that it exposes the code in the output of ps -Awww
on both the local and remote system.
¹ as in that case, it's sshd
that does bash -c thatcode
, and at least in current versions of openssh, it doesn't do bash -c -- thatcode
(possibly because not all shells support or need that --
or more likely because the issue (unlikely to happen in practice) has been overlooked; that same issue for C's system()
has been overlooked for many decades)
readEnterKey
routine, and also there isn't the exit condition of thewhile
loop and it's closing. – aviro Jun 13 '23 at 11:39scp
to copy it to the remote host (perhaps to /tmp), and then usingssh
to run it. This also avoids issues with stdin being redirected. – cas Jun 14 '23 at 03:01