I have a password function that I borrowed from How do I echo stars (*) when reading password with read?
I tried to adapt it so that I can run through the function twice to do a password confirmation and then evaluate the 2 passwords to determine if they match but I seem to be missing some basics of how bash works in this case.
I tried replacing PASSWORD with $1 but kept getting command not found errors
passWord() {
unset PASSWORD
unset CHARCOUNT
stty -echo
CHARCOUNT=0
while IFS= read -p "$PROMPT" -r -s -n 1 CHAR; do
# Enter - accept password
if [[ $CHAR == $'\0' ]] ; then
break
fi
# Backspace
if [[ $CHAR == $'\177' ]] ; then
if [ $CHARCOUNT -gt 0 ] ; then
CHARCOUNT=$((CHARCOUNT-1))
PROMPT=$'\b \b'
PASSWORD="${PASSWORD%?}"
else
PROMPT=''
fi
else
CHARCOUNT=$((CHARCOUNT+1))
PROMPT='*'
PASSWORD+="$CHAR"
fi
done
stty echo; echo
${1}=${PASSWORD}
}
echo -n "Enter the password > "
passWord passOne
echo -n "Please re-enter the password > "
passWord passTwo
if [[ $passOne == $passTwo ]]; then
PASSWORD=$passOne
else
echo "Passwords did not match, please try again."
fi
Update
Here is the script with the latest updates
#!/bin/bash
passWord() {
unset password
local prompt char
stty -echo
charcount=0
while IFS= read -p "$prompt" -r -s -n 1 CHAR; do
# Enter - accept password
if [[ $char == $'\0' ]] ; then
break
fi
# Backspace
if [[ $char == $'\177' ]] ; then
if [ $charcount -gt 0 ] ; then
charcount=$((CHARCOUNT-1))
prompt=$'\b \b'
password="${password%?}"
else
prompt=''
fi
else
charcount=$((charcount+1))
prompt='*'
password+="$char"
fi
done
stty echo; echo
}
echo -n "Enter the password > "
passWord
pass1=$password
echo -n "Please re-enter the password > "
passWord
pass2=$password
if [[ "$pass1" == "$pass2" ]]; then
PassWord=$pass1
else
echo "Passwords did not match, please try again."
fi
You are missing a declaration of your shell.
Please add a shebang as the first line:
#!/bin/bash
The assignment of variables (the line ${1}=${PASSWORD}) doesn't work.
One way to solve it (not recomended) is to add eval:
eval "${1}=${PASSWORD}" # don't use quite risky.
But as that makes any input a security issue, you should use some other line.
One solution is to use declare (bash 4.2+):
declare -g "${1}=${PASSWORD}"
The -g is required (required and available since bash 4.2) to change General variables (not local to the function).
Or use printf (since bash 3.1):
printf -v "${1}" '%s' "${PASSWORD}"
Other than that, you should add a local command for variables used inside the function to avoid conflicts with external variables and should add a PROMPT='' just before the loop to avoid the printing of an initial asterisk when calling the function a second time.
It should be said that using variables in CAPS should be avoided. Variables in CAPS denote environment variables, the rest of variables use lower case to avoid conflicts.
Related
I am trying to write a program where the user can enter a username and password, then the code should check if the username and password are correct unfortunately whenever it checks no matter if the username/password is correct of not it will echo "Username Verified".
#!/bin/bash
echo "Username"
read username
echo "Password"
read password
sleep 1
correct_u=user
correct_p=pass
if [[ $username -eq $correct_u ]]
then
echo "Username Verified..."
else
echo "Username Incorect..."
fi
sleep 1
if [[ $correct_p -eq $password ]]
then
sleep 1
echo "Password Verified..."
else
echo "Password Incorect..."
fi
I have tired checking that all the variables work
Unless username and correct_u consist solely of digits, [[ $username -eq $correct_u ]] will always evaluate to true, since -eq forces the arguments to be numbers, and if there are no number, the arguments are treated as zero.
To do a string comparision, do
[[ $username == "$correct_u" ]]
Quoting the right-hand side is important here, to avoid that it is interpreted as glob-pattern, since == in general does a wildcard match.
You should use = instead of -eq when comparing strings in bash. = is used for string comparison while -eq is used for integer comparison:
#!/bin/bash
echo "Username"
read username
echo "Password"
read password
sleep 1
correct_u=user
correct_p=pass
if [[ "$username" = "$correct_u" ]]
then
echo "Username Verified..."
else
echo "Username Incorrect..."
fi
sleep 1
if [[ "$correct_p" = "$password" ]]
then
sleep 1
echo "Password Verified..."
else
echo "Password Incorrect..."
fi
Embedding your name & pw in cleartext in the file isn't ideal. The user ID must be able to read it to execute the commands in it; executable permissions won't help if you take away read.
Use that to your advantage. Set the user/group/world permissions appropriately. Then the user and password are entered at login...
You might want to combine methods from here and here, reading the right password securely from a vault file and comparing that to the one you read silently from the user.
But first, as already mentioned - fix your test.
$: [[ "one" -eq "two" ]] && echo same || echo no
same
$: [[ "one" == "two" ]] && echo same || echo no
no
$: [[ "one" == "one" ]] && echo same || echo no
same
My property file:
a.prop
user=abc
location=home
user=xyz
location=roamer
I need to read a.prop and keep user and location inside a variable so that I can pass them to my other script (check.sh) as an argument.
The check.sh needs to be called for all the list of user/location.
I don't want to use AWK
Here's an extremely fragile, ill-advised solution that invokes a function for each stanza of your config, but requires the exact format of your input and is vulnerable to many, many attack vectors. Use (or, preferrably not) at your own risk!
#!/bin/bash
foo() { echo "user is $user, location is $location"; }
eval "$(sed -e '/^$/s//foo/' input; echo foo)"
You would be much better off pre-processing the input, and a willingness to use awk would be helpful.
untested
while read -r line; do
key=${line%%=*} # the left-hand-side of the =
case $key in
user) user=${line#*=} ;;
location) location=${line#*=} ;;
*) continue ;; # skip this line
esac
if [[ -n $user ]] && [[ -n $location ]]; then
echo "have user=$user and location=$location"
check.sh "$user" "$location"
unset user location
fi
done < a.prop
This version is a little unsafer: just assume the properties are valid shell variable assignments.
while read -r line; do
[[ $line != *=* ]] && continue
declare "$line"
if [[ -n $user ]] && [[ -n $location ]]; then
echo "have user=$user and location=$location"
check.sh "$user" "$location"
unset user location
fi
done < a.prop
Or, assuming "user" always appears before "location"
grep -E '^(user|location)=' a.prop |
while read userline; read locline; do
declare "$userline"
declare "$locline"
echo "have user=$user and location=$location"
check.sh "$user" "$location"
done
Is it possible to store something - for example user input - into a variable in bash? I have a script that reads a password...
#!/bin/bash
while [ $# -gt 0 ]; do
key="$1";
case $key in
-p|--prompt)
if [ ! "$2" ]; then
printf "\e31;1mMust pass in a prompt\e[0m\n";
exit 1;
fi
printf "$2";
shift 2;
;;
esac
done
psswd=;
c=;
while [ "$c" != "^M" ]; do
read -n 1 -s c;
if [ "$c" == "^?" ]; then
if [ ${#psswd} -gt 0 ]; then
psswd=${psswd:: -1};
printf "\b \b";
fi
elif [ ! "$c" ]; then # pressed enter
break;
else
psswd="$psswd$c";
printf "*";
fi
done;
printf "\nYour entered password is: $psswd\n"
(sorry, ^M is newline and ^? is backspace. Meaning you can't copy and paste, but that is how it shows up in vim, and I don't know how else to write it.)
... and I would like the password to be strored into a variable. You know how read stores a variable. If read can do it, why can't I? Does anyone have any ideas?
(Or perhaps there is a better way to store the password... like echo "$psswd" | base64 > .pass - which isn't really what I want to do.)
UPDATE
Basically, how do I get my psswd into a variable outside the shell? OR how do I access the password once that shell is closed? Is there a better way to store it other than a variable?
As rojomoke suggests, this is enough:
read -p "Enter your password: " -s pw
echo "You entered: $pw"
If you do this: read -n 1 -s c and you just hit Enter, then $c will be empty, it will not hold \r; and if you hit backspace, in my testing, $c holds a character with octal value 177
read -n 1 -s c
printf "%s" "$c" | od -c
I have many Yes/No answers in my script.
How can I create a function to minimize the size of my script?
I have the following:
function ask {
read -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
return 1;
else
exit
echo "Abort.."
fi
}
ask "Continue? [y/N] "
It works fine. But the Question "Continue? [y/N] is not displayed. How can I "transfer" this text to my function
You can use $1 variable:
function ask {
echo $1 # add this line
read -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
return 1;
else
exit
echo "Abort.."
fi
}
Edit: as noted by #cdarke, 'echo' call can be avoided thanks to '-p' switch in read:
# echo $1
# read -n 1 -r
read -n 1 -r -p "$1"
What do I need to do for code in Bash, if I want to echo *s in place of password characters (or even just hide the characters completely) when the user types something in using read?
As Mark Rushakoff pointed out, read -s will suppress the echoing of characters typed at the prompt. You can make use of that feature as part of this script to echo asterisks for each character typed:
#!/bin/bash
unset password
prompt="Enter Password:"
while IFS= read -p "$prompt" -r -s -n 1 char
do
if [[ $char == $'\0' ]]
then
break
fi
prompt='*'
password+="$char"
done
echo
echo "Done. Password=$password"
I really liked the answer that Wirone gave, but I didn't like that the backspacing would continue removing characters even back into the "Enter password: " prompt.
I also had some issues where pressing keys too rapidly would cause some of the characters to actually print on the screen... never a good thing when prompting for a password. =)
The following is my modified version of Wirone's answer which addresses these issues:
#!/bin/bash
unset PASSWORD
unset CHARCOUNT
echo -n "Enter password: "
stty -echo
CHARCOUNT=0
while IFS= read -p "$PROMPT" -r -s -n 1 CHAR
do
# Enter - accept password
if [[ $CHAR == $'\0' ]] ; then
break
fi
# Backspace
if [[ $CHAR == $'\177' ]] ; then
if [ $CHARCOUNT -gt 0 ] ; then
CHARCOUNT=$((CHARCOUNT-1))
PROMPT=$'\b \b'
PASSWORD="${PASSWORD%?}"
else
PROMPT=''
fi
else
CHARCOUNT=$((CHARCOUNT+1))
PROMPT='*'
PASSWORD+="$CHAR"
fi
done
stty echo
echo $PASSWORD
read -s should put it in silent mode:
-s Silent mode. If input is coming from a terminal, characters are not echoed.
See the read section in man bash.
I would like to add something to Dennis Williamson's solution:
#!/bin/bash
unset password
echo -n "Enter password: "
while IFS= read -p "$prompt" -r -s -n 1 char
do
# Enter - accept password
if [[ $char == $'\0' ]] ; then
break
fi
# Backspace
if [[ $char == $'\177' ]] ; then
prompt=$'\b \b'
password="${password%?}"
else
prompt='*'
password+="$char"
fi
done
In above example script handles backspace correctly.
Source
I don't know about stars, but stty -echo is your friend:
#!/bin/sh
read -p "Username: " uname
stty -echo
read -p "Password: " passw; echo
stty echo
Source: http://www.peterbe.com/plog/passwords-with-bash
If you don't care about it being interactive, you can simply do
read -s pass
echo "$pass" | sed 's/./*/g'
This will show a * for each character of the entered password after enter is pressed.
stty -echo
read something
stty echo
will stop user input being echoed to the screen for that read. Depending on what you are doing with prompts, you may want to add an extra echo command to generate a newline after the read.
I just made this Bash-specific function based on Dennis Williamson's, Wirone's and Logan VanCuren's answers:
ask() {
local 'args' 'char' 'charcount' 'prompt' 'reply' 'silent'
# Basic arguments parsing
while [[ "${1++}" ]]; do
case "${1}" in
( '--silent' | '-s' )
silent='yes'
;;
( '--' )
args+=( "${#:2}" )
break
;;
( * )
args+=( "${1}" )
;;
esac
shift || break
done
if [[ "${silent}" == 'yes' ]]; then
for prompt in "${args[#]}"; do
charcount='0'
prompt="${prompt}: "
reply=''
while IFS='' read -n '1' -p "${prompt}" -r -s 'char'; do
case "${char}" in
# Handles NULL
( $'\000' )
break
;;
# Handles BACKSPACE and DELETE
( $'\010' | $'\177' )
if (( charcount > 0 )); then
prompt=$'\b \b'
reply="${reply%?}"
(( charcount-- ))
else
prompt=''
fi
;;
( * )
prompt='*'
reply+="${char}"
(( charcount++ ))
;;
esac
done
printf '\n' >&2
printf '%s\n' "${reply}"
done
else
for prompt in "${args[#]}"; do
IFS='' read -p "${prompt}: " -r 'reply'
printf '%s\n' "${reply}"
done
fi
}
It could be used like:
$ ask Username
Username: AzureDiamond
AzureDiamond
$ ask -s Password
Password: *******
hunter2
$ ask First Second Third
First: foo
foo
Second: bar
bar
Third: baz
baz
#nxnev's answer didn't quite work for me, at least on macOS. I simplified it a bit, and now it's flawless:
#!/bin/bash
ask() {
charcount='0'
prompt="${1}: "
reply=''
while IFS='' read -n '1' -p "${prompt}" -r -s 'char'
do
case "${char}" in
# Handles NULL
( $'\000' )
break
;;
# Handles BACKSPACE and DELETE
( $'\010' | $'\177' )
if (( charcount > 0 )); then
prompt=$'\b \b'
reply="${reply%?}"
(( charcount-- ))
else
prompt=''
fi
;;
( * )
prompt='*'
reply+="${char}"
(( charcount++ ))
;;
esac
done
printf '\n' >&2
printf '%s\n' "${reply}"
}
pwd="$(ask Password)"
echo "Your password is $pwd"
#!/bin/bash
echo "------------------------------"
n=7
echo " Enter Password :"
for (( i=1;i<n;i++ ))
do
stty -echo
read -r -s -n 1 char
stty echo
echo -n "*"
pass+="$char"
done
echo " "
echo " Your password : $pass "
echo ""
echo "-------------------------------"