How to prompt and hide characters while using read command [duplicate] - bash

This question already has answers here:
Hiding user input on terminal in Linux script
(9 answers)
Closed 3 years ago.
I am creating a script that is used for creating an user account. I want to hide the characters of the password while the user is prompted to enter the password
I tried to use read -ps but I get
./account.sh: line 26: read: `Password: ': not a valid identifier
Here is my code
#!/bin/bash
# Check if user is Root
if [[ ${UID} -ne '0' ]]
then
echo 'You are not autharized for this process'
exit
fi
# Ask for full name
read -p 'Full Name: ' full_name # Set the input data to "full_name" variable
# Ask for user name
read -p 'User Name: ' user_name # Set the input data to "user_name" variable
# Check for user name length
username_length=${#user_name}
if [[ ${username_length} -lt 3 ]]
then
echo 'User name should be at least 3 characters'
exit
fi
# Ask for password
read -ps 'Password: ' password # Set the input data to "password" variable
# Check for password length
password_length=${#password}
if [[ ${password_length} -lt 8 ]]
then
echo 'Password should be at least 8 characters'
exit
fi
# Create the user
useradd -c "${comment}" -m ${user_name}
# Set the password for the user
echo -e "$password\n$password" | passwd "$user_name"
# Change password on first login
passwd -e ${user_name}

for your example you could use
read -p -s 'Password: ' password
ps friendly reminder to never store passwords in plain text

Related

Parse the output of a script and process the last line only before printing

I have a simple CLI tool asking for a master password, and printing a string $USER $PASSWORD only if the master password is correct.
How to reproduce?
Here is a script just for demonstrating my use-case, the real script is in fact a CLI tool on which I have no control:
#!/usr/bin/env sh
printf "Enter master password: "
read -s password
echo
[ "$password" == "MasterPassword" ] && echo "user1 Passw0rd!"
Example of usage:
$ ./my-cli-tool
Enter master password: ********
user1 Passw0rd!
Issue
I don't want the password (Passw0rd!) to be printed on screen. I want to print only the user (user1), and just copy the password (Passw0rd!) to the clipboard (let's say with xclip -sel clipboard).
What I have tried?
If the first line (Enter master password) were not there, I would have done:
./my-cli-tool |
while read -r USER PASSWORD
do
echo $USER
echo -n $PASSWORD | xclip -sel clipboard
done
But my issue is that I should type the master password when the prompt asks for, and so the first line is always printed. I have tried to run ./my-cli-tool | tail -1: the prompt is not shown, although if I type the master password, it only prints user1 Passw0rd!, so I can do the command above to copy the password into the clipboard.
Question
Do you have any idea to:
always show the prompt on screen for the master password
only print the user
copy the password to the clipboard
Expected output
Basically, I would like that kind of output:
$ ./my-cli-tool | solution
Enter master password: ********
user1
And have Passw0rd! copied into my clipboard.
I've simply modified your answer a little bit -
./my-cli-tool | {
x=$(dd bs=1 count=1 2>/dev/null)
while [ "$x" != : ]; do
printf %c "$x";
x=$(dd bs=1 count=1 2>/dev/null)
done
printf %s ": "
while read -r USER PASSWORD
do
echo $USER
echo -n $PASSWORD | xclip -sel clipboard
done
}
Lemme know if it works.
EDIT: Updated logic. Uses dd.

Bash script that asks for password to view passwords file

I'm trying to create a bash script that asks for password when you try to see the password file, but I'm stucked. This is my code:
#!/bin/bash
# Read Password
echo -n Password:
read -s PASSWORD
passwords() {
echo "
PASSWORDS
"
}
if [ "$PASSWORD"="root" ]; then
passwords
exit
else
echo "Wrong password"
exit
fi
I've tried a lot of things, for example if [ "$PASSWORD"!="root" ] instead of else but none of them worked.
Here is a shorter version:
#!/bin/bash
passwords(){
echo "PASSWORDS"
}
## Read Password
read -p "Enter password: " -s PASSWORD
desired_password="root"
[ "$PASSWORD" == "$desired_password" ] && passwords || echo "Wrong password"
As #vdavid said, you can add a space around the equal sign or even better, as you have bash shell, it is recommended to use double-bracket for your if statement. Check this: Is there any difference between '=' and '==' operators in bash or sh
Also you can add:
printf "/n" so your script will behave like a typical Linux prompt for password - information will output in new line
non-zero exit code in case of wrong password (exit 1)
Basically, after those improvements code looks like this:
#!/bin/bash
# Read Password
echo -n Password:
read -s PASSWORD
printf "\n"
passwords() {
echo "PASSWORDS"
}
if [[ "$PASSWORD" == "root" ]]; then
passwords
exit 0
else
echo "Wrong password"
exit 1
fi
Note that I used "==" instead of "=", but for double-bracket they both do the same job.

username, password program in bash

I have a program that asks input from user on their username and password then stores it in a text file column one is usernames and column 2 is passwords, i need a command that replaces the password when the user inputs their username and new password, heres what i have
#!/bin/bash
#admin menu
#Register User
echo enter the username of the user you want to register.
read reguser
echo enter the password of the user you want to register.
read regpass
User_Pass="username_pass.txt"
if [ ! -e "$User_Pass" ];
then
echo "Creating Username and Passwords file"
touch $User_Pass
fi
echo "$reguser $regpass" | cat >> $User_Pass
echo user succesfully registered.
#Change Password
echo "Enter the username you want to change the password for"
read change1
change2=$(grep -q $change1 username_pass.txt)
if [ $change2=0 ];
then
echo enter your new password
read newpass
awk -v newpass="$newpass" -v change1="$change1" '$1 ~ change1 {$2 = newpass}' username_pass.txt
#i tried this but it didnt work
echo "password changed!"
else
echo "no such username."
fi
You can use sed
sed -i "s/$change1.*/$change1 $newpass/" username_pass

Until user input equals something do

So somebody showed me how to use condition(s) to test if a user had typed input for a password.
I wanna take a their example a step further and use a loop (at least thats what I think it's call).
Here is their example:
read -s -p "Enter new password: " NEWPASS
if test "$NEWPASS" = ""; then
echo "Password CAN NOT be blank re-run sshd_config"
exit 1;
fi
Instead of exiting the script I want it to keep asking for the input until there is some.
I wanna make a statement like this but I was doing it wrong:
(Now using top example)
The user is given a chance to enter new password and fill the variable value.
read -s -p "Enter new password:" NEWPASS
echo ""
The next portion checks the variable to see if it contains a value, while the value is null it request the user to fill the value indefinitely. ( a loop)
while [[ -z "$NEWPASS" ]]; do
echo ""
echo "Password CAN NOT be blank"
echo ""
read -s -p "Enter new password:" NEWPASS;
echo ""
done
This line searches a file containing a set of variables used by another file. It then searches the file for a line containing PASS=.* (.* meaning anything) then writes PASS=$NEWPASS ($NEWPASS being variable)
sed -i -e"s/^PASS=.*/PASS=$NEWPASS/" /etc/sshd.conf
Thank you for all the help, I'm going to use this and learn from it.
This while loop should work:
while [[ -z "$NEWPASS" ]]
do
read -s -p "Enter new password: " NEWPASS
done
while read -s -p 'Enter new password: ' NEWPASS && [[ -z "$NEWPASS" ]] ; do
echo "No-no, please, no blank passwords!"
done

Shell syntax error near unexpected token `done'

This shell script is supposed to add users to the system. The new users details are in a file. The shell is rejecting this script with the message:
syntax error near unexpected token 'done'.
What's wrong?
#!/bin/bash
#Purpose: Automatically add new users in a linux system based upon the data found within a text file
# Assign encryped passwords to each user
# Add users to groups or create new groups for these users
# Report errors and successful operations where necessary in log files
# post help options (echo)
#Root validation
if [[ $(id -u) -eq 0 ]]; then
#Argument validation
if [[ -z "$1" ]]; then
echo "No arguments found!"
echo "Please include a user detail text file as first argument"
echo "Please include a report text file as second argument"
echo "Please include an error report text file as the third argument"
echo "Use the -h argument (i.e. ./script -h) for help"
exit 1
fi
#Help validation and Help file
if [[ "$1" = "-h" ]]; then
echo "This is the help information file"
echo "This script is designed to add users to a linux system by reading information from a user detail file (such as userlist.txt)"
echo "The format of this user detail text file is "username password groupname fullname" seperated using TAB spacing"
echo "This script will read the first argument as the user detail file, the second and third arguments will be read as a success report file and error report file respectively"
exit
fi
#Reads first argument as user detail file for data
cat userlist.txt | while read uname password gname fullname
#Reads /etc/passwd for Username
egrep -w "^$uname" /etc/passwd
#If Username is found then error reports
if [ $? == 0 ]; then
echo "User Already Exists : Error adding user with username $uname;$gname;$fullname" >> Successes1.log
exit 1
else
#Reads /etc/group for Groupname
egrep -w "^$gname" /etc/group
#If Groupname is found then nothing
if [ $? == 0 ]; then
echo ""
else
#If Groupname not found then creates new group and reports
groupadd "$gname"
echo "Group Not Found: New Group $gname was created" >> Successes1.log
fi
#Retrieves Date
createddate=$(date)
#Perl password script takes input from Userlist
pass=$(perl -e 'print crypt($ARGV[0], "Password")' "$password")
#Adds Users with variables from userlist
useradd "$uname" -g "$gname" -c "$fullname" -p "$pass"
#Reports information to successlist and errorlist report files
if [ $? == 0 ]; then
groupid=$(id -g $uname)
userid=$(id -u $uname)
echo "User Successfully Added: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Successes1.log
else
groupid=$(id -g $uname)
userid=$(id -u $uname)
echo "Useradd Error Occurred: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Errors1.log
echo "Error: Must be root user to execute script"
exit
fi
done
Second attempt
Using some of the ideas from the answers, I came up with a second attempt:
#!/bin/bash
#Purpose: Automatically add new users in a linux system based upon the data found within a text file
# Assign encryped passwords to each user
# Add users to groups or create new groups for these users
# Report errors and successful operations where necessary in log files
# post help options (echo)
#Root validation
if [[ $(id -u) -eq 0 ]]; then
#Argument validation
if [[ -z "$1" ]]; then
echo "Usage: $0 usernames report errors" 1>&2
echo "Please include a user detail text file as first argument"
echo "Please include a report text file as second argument"
echo "Please include an error report text file as the third argument"
echo "Use the -h argument (i.e. ./script -h) for help"
exit 1
fi
fi
#Help validation and Help file
if [[ "$1" = "-h" ]]; then
echo "This is the help information file"
echo "This script is designed to add users to a linux system by reading information from a user detail file (such as userlist.txt)"
echo "The format of this user detail text file is "username password groupname fullname" seperated using TAB spacing"
echo "This script will read the first argument as the user detail file, the second and third arguments will be read as a success report file and error report file respectively"
exit
fi
#Reads first argument as user detail file for data
cat jan.txt | while read uname password gname fullname; do
#Reads /etc/passwd for Username
egrep -w "^$uname:" /etc/passwd >/dev/null 2>&1
#If Username is found then error reports
if [ $? == 0 ]
then
echo "User Already Exists : Error adding user with username $uname;$gname;$fullname" >> Errors1.log
else
#Reads /etc/group for Groupname
egrep -w "^$gname" /etc/group
#If Groupname is found then nothing
if [ $? == 0 ]; then
echo ""
else
#If Groupname not found then creates new group and reports
groupadd "$gname"
echo "Group Not Found: New Group $gname was created" >> Successes1.log
done < $1
#Retrieves Date
createddate=$(date)
#Perl password script takes input from Userlist
pass=$(perl -e 'print crypt($ARGV[0], "Password")' "$password")
#Adds Users with variables from userlist
useradd "$uname" -g "$gname" -c "$fullname" -p "$pass"
#Reports information to successlist and errorlist report files
if [ $? == 0 ]
then
groupid=$(id -g $uname)
userid=$(id -u $uname)
echo "User Successfully Added: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Successes1.log
else
groupid=$(id -g $uname)
userid=$(id -u $uname)
echo "Useradd Error Occurred: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Errors1.log
echo "Error: Must be root user to execute script"
exit 1
fi
fi
done
This does not seem to work properly either. What's wrong now?
Seems to show arguments and runs however no users nor group have been added therefore no logs have been created
The if starting at:
if [ $? == 0 ]; then
echo "User Already Exists : Error adding user with username ...
exit 1
else
is ended with the done instead of the fi that is required.
The while loop starting a couple of lines earlier:
cat userlist.txt | while read uname password gname fullname
is missing its do (another bug); if that was present, then it would also need the done at the end. Someone lost track of the indentation. (Using 2 characters per level is better than 0 or 1, but it is easier to track levels if you use 4 spaces per level.) Note that the shell hasn't gotten around to complaining about the lack of do because the syntax for a while loop is:
while cmd1
cmd2
cmd3 ...
do
and as far as the shell is concerned, it is still processing commands in the list cmd1, cmd2, cmd3, ....
Here's a semi-decently indented version of the script. There was a missing fi at the top of the script, too.
#!/bin/bash
#Purpose: Automatically add new users in a linux system based upon the data found within a text file
# Assign encryped passwords to each user
# Add users to groups or create new groups for these users
# Report errors and successful operations where necessary in log files
# post help options (echo)
#Root validation
if [[ $(id -u) -eq 0 ]]
then
#Argument validation
if [[ -z "$1" ]]
then
echo "No arguments found!"
echo "Please include a user detail text file as first argument"
echo "Please include a report text file as second argument"
echo "Please include an error report text file as the third argument"
echo "Use the -h argument (i.e. ./script -h) for help"
exit 1
fi
fi
#Help validation and Help file
if [[ "$1" = "-h" ]]
then
echo "This is the help information file"
echo "This script is designed to add users to a linux system by reading information from a user detail file (such as userlist.txt)"
echo "The format of this user detail text file is "username password groupname fullname" seperated using TAB spacing"
echo "This script will read the first argument as the user detail file, the second and third arguments will be read as a success report file and error report file respectively"
exit
fi
#Reads first argument as user detail file for data
cat userlist.txt | while read uname password gname fullname
do
#Reads /etc/passwd for Username
egrep -w "^$uname" /etc/passwd
#If Username is found then error reports
if [ $? == 0 ]
then
echo "User Already Exists : Error adding user with username $uname;$gname;$fullname" >> Successes1.log
exit 1
else
#Reads /etc/group for Groupname
egrep -w "^$gname" /etc/group
#If Groupname is found then nothing
if [ $? == 0 ]
then
echo ""
else
#If Groupname not found then creates new group and reports
groupadd "$gname"
echo "Group Not Found: New Group $gname was created" >> Successes1.log
fi
#Retrieves Date
createddate=$(date)
#Perl password script takes input from Userlist
pass=$(perl -e 'print crypt($ARGV[0], "Password")' $pass)
#Adds Users with variables from userlist
useradd "$uname" -g "$gname" -c "$fullname" -p "$pass"
#Reports information to successlist and errorlist report files
if [ $? == 0 ]
then
groupid=$(id -g $uname)
userid=$(id -u $uname)
echo "User Successfully Added: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Successes1.log
else
groupid=$(id -g $uname)
userid=$(id -u $uname)
echo "Useradd Error Occurred: $uname;$userid;$gname;$groupid;$createddate;$fullname" >> Errors1.log
echo "Error: Must be root user to execute script"
exit
fi
fi
done
There is still much room for improvement. The root validation block should exit if the user is not root; that happens instead a mile further down inside the loop. You can check the number of arguments better: $# gives you the number of arguments. If I tried yourscript.sh '' arg2 arg3, you'd claim there were no arguments when in fact the problem is that $1 is present but is an empty string. The standard convention for reporting how to use a command is something like:
echo "Usage: $0 usernames report errors" 1>&2
This reports the command's name, and the arguments expected. The 1>&2 sends the message to standard error instead of standard output. The logic here is a little bizarre even so. You check that the user is root and only then check that there are arguments. If the user is not root, you don't check the arguments. Not entirely sensible, I submit.
We can debate the UUOC (Useless Use of Cat). There's actually an award for it; I don't think this qualifies. However, it would be possible to write:
while read uname password gname fullname
do
...
done < $1
Hmmm...the script is supposed to take a file name argument that specifies the users, but the cat takes a fixed file name, not the file name argument!
Similarly, arguments 2 and 3 are studiously ignored; the log files are hard-coded.
egrep -w "^$uname" /etc/passwd
#If Username is found then error reports
if [ $? == 0 ]
This fragment can be improved several ways:
if egrep -w "^$uname:" /etc/passwd >/dev/null 2>&1
then
#If Username is found then error report
This tests the exit status of the egrep command directly; it also prevents a new user roo from being treated as pre-existing because of user root. It sends the output and error output to /dev/null so that you won't see anything when the user does exist.
It might be better not to exit when the user name is found; you could at least try to process the next entry. It is also odd that the report that the user exists (which terminates the processing) is recorded in Successes1.log rather than in Errors1.log; it is treated like an error.
The group check constructs are similar and should be similarly upgraded.
You read the password into $password with the read line; when it comes to creating the password, though, you have:
pass=$(perl -e 'print crypt($ARGV[0], "Password")' $pass)
On the first cycle, $pass is empty (most probably); you should have used $password in double quotes at the end:
pass=$(perl -e 'print crypt($ARGV[0], "Password")' "$password")
As with the egrep commands, you can check the status of the useradd command directly too. It is a bit sweeping to say if [ $? == 0 ] is the mark of a tyro, but it isn't too far off the truth.
The final exit in the script should be exit 1 to indicate an error exit. As already noted, this is preceded by the comment about 'you must be root', even though there was a check at the top for root privileges.
Caveat: I've not attempted to run the script; I could easily have missed some issues. It does, however, pass sh -v -n, so there are no gross syntactic errors left.
Once a shell script is syntactically correct, then you normally debug it using sh -x script (or, if it takes arguments, then sh -x script arg1 arg2 arg3 ...). This is the execution trace mode. The shell tells you, more or less inscrutably, what it is doing. The information is written to standard error. You can even trap the output for later scrutiny if you like with:
sh -x script arg1 arg2 arg3 2>script-x.log
The 2>script-x.log notation sends the standard error to the file script-x.log (choose your own meaningful name; I often use x or xxx for files I won't want to keep, but I also remove such files without necessarily even looking at them because I know they are throwaway files).
"done" is supposed to be paired with a previous "do"
It does not man th end of your script.
Your while is missing a do:
cat userlist.txt | while read uname password gname fullname
do
or
cat userlist.txt | while read uname password gname fullname; do

Resources