Repeat an IF statement until condition is met [duplicate] - bash

I have a script and want to ask the user for some information, but the script cannot continue until the user fills in this information. The following is my attempt at putting a command into a loop to achieve this but it doesn't work for some reason:
echo "Please change password"
while passwd
do
echo "Try again"
done
I have tried many variations of the while loop:
while `passwd`
while [[ "`passwd`" -gt 0 ]]
while [ `passwd` -ne 0 ]]
# ... And much more
But I can't seem to get it to work.

until passwd
do
echo "Try again"
done
or
while ! passwd
do
echo "Try again"
done

To elaborate on #Marc B's answer,
$ passwd
$ while [ $? -ne 0 ]; do !!; done
Is nice way of doing the same thing that's not command specific.

You need to test $? instead, which is the exit status of the previous command. passwd exits with 0 if everything worked ok, and non-zero if the passwd change failed (wrong password, password mismatch, etc...)
passwd
while [ $? -ne 0 ]; do
passwd
done
With your backtick version, you're comparing passwd's output, which would be stuff like Enter password and confirm password and the like.

If anyone looking to have retry limit:
max_retry=5
counter=0
until $command
do
sleep 1
[[ counter -eq $max_retry ]] && echo "Failed!" && exit 1
echo "Trying again. Try #$counter"
((counter++))
done

You can use an infinite loop to achieve this:
while true
do
read -p "Enter password" passwd
case "$passwd" in
<some good condition> ) break;;
esac
done

while [ -n $(passwd) ]; do
echo "Try again";
done;

Related

My bash script verification code does not work

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

Try/Except loop in shell [duplicate]

I have a script and want to ask the user for some information, but the script cannot continue until the user fills in this information. The following is my attempt at putting a command into a loop to achieve this but it doesn't work for some reason:
echo "Please change password"
while passwd
do
echo "Try again"
done
I have tried many variations of the while loop:
while `passwd`
while [[ "`passwd`" -gt 0 ]]
while [ `passwd` -ne 0 ]]
# ... And much more
But I can't seem to get it to work.
until passwd
do
echo "Try again"
done
or
while ! passwd
do
echo "Try again"
done
To elaborate on #Marc B's answer,
$ passwd
$ while [ $? -ne 0 ]; do !!; done
Is nice way of doing the same thing that's not command specific.
You need to test $? instead, which is the exit status of the previous command. passwd exits with 0 if everything worked ok, and non-zero if the passwd change failed (wrong password, password mismatch, etc...)
passwd
while [ $? -ne 0 ]; do
passwd
done
With your backtick version, you're comparing passwd's output, which would be stuff like Enter password and confirm password and the like.
If anyone looking to have retry limit:
max_retry=5
counter=0
until $command
do
sleep 1
[[ counter -eq $max_retry ]] && echo "Failed!" && exit 1
echo "Trying again. Try #$counter"
((counter++))
done
You can use an infinite loop to achieve this:
while true
do
read -p "Enter password" passwd
case "$passwd" in
<some good condition> ) break;;
esac
done
while [ -n $(passwd) ]; do
echo "Try again";
done;

Bash confirmation won't wait for user input

I am trying to implement confirmation prompt with a bash script but for some reason, prompt won't wait for user input. I've tried many examples but no luck so far. I am on MacOS if it makes any difference.
Just a few examples I tried (All copy+paste from other answers in SO):
#!/bin/bash
read -p "Are you sure? " -n 1 -r
echo # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
# do dangerous stuff
fi
#!/bin/bash
read -p "Continue (y/n)?" CONT
if [ "$CONT" = "y" ]; then
echo "yaaa";
else
echo "booo";
fi
#!/bin/bash
while true; do
read -rsn1 input
if [ "$input" = "a" ]; then
echo "hello world"
fi
done
#!/bin/bash
read -p "Continue (y/n)?" choice
case "$choice" in
y|Y ) echo "yes";;
n|N ) echo "no";;
* ) echo "invalid";;
esac
This doesn't even prompt anything:
#!/bin/bash
read -n 1 -s -r -p "Press any key to continue"
Changed to answer from comment : in commit-msg hook it seems standard input is closed, indeed this can be checked adding following command
ls -l /dev/fd/
which gives
... 0 -> /dev/null
as mentioned in this post
exec 0< /dev/tty
will restore standard input to tty, another solution as noticed standard output and error are still redirected to tty
exec 0<&1
The original question has the important part missing and it is my fault not making it very clear in very first place. It became apparent after #NahuelFouilleul's comment. The confirmation/question prompt was not waiting for user to hit a key. The reason was because my bash script was being called by a git hook. Things seem to be done in slightly different way in such cases. The solution is below but the original answer is here.
#!/bin/bash
exec < /dev/tty
while true; do
read -p "Accepting the offer? (y/n) " answer
if [[ $answer =~ ^[Yy]$ ]] ;
then
echo "Accepted"
else
echo "Not accepted"
fi
break
done
Try this:
echo -n "Continue (y/n)?"
read CONT
if [ "$CONT" = "n" ]
then
echo "NO"
else
echo "YES"
fi
the echo -n means no newline

Bash while loop

I'm trying to make a while loop.
This loop should either echo "try again or type exit to quit" when typing gibberish or finger a user if typed the user name.
echo Please enter a user name to find.
read username
done_fn()
{
finger $username
exit 0
}
continue_fn()
{
echo 'try again or type exit to quit'
read exitvar
}
grep $username /etc/passwd >/dev/null
while [ $? -eq 0 ]
do
done_fn
done
exitvar=quit
until [ $exitvar = exit ]
do
continue_fn
done
while [ -u $exitvar ]
do
done_fn
done
This is everything I have got so far. Whenever I I type in a username after 'try again or type exit to quit' it will just echo it again. I would like it to finger the user instead of echoing it again. I have looked in so many places and can't find the answer.
Thank you in advance.
You can try this:
typeset u="John Doe"
while ! grep -q "^$u:" /etc/passwd
do
echo -n "username? "
read u
if [[ $u = "q" ]]
then
exit
fi
done
finger $u
echo Please enter a user name to find.
read username
donev2_fn()
{
finger $exitvar
exit 0
}
done_fn()
{
finger $username
exit 0
}
continue_fn()
{
echo 'try again or type exit to quit'
read exitvar
grep $exitvar /etc/passwd >/dev/null
if [ $? -eq 0 ]
then
donev2_fn
fi
}
grep $username /etc/passwd >/dev/null
while [ $? -eq 0 ]
do
done_fn
done
exitvar=quit
until [ $exitvar = exit ]
do
continue_fn
done
I figured it out. I had to put an extra function in it. Thank you for all the help.

Check Whether a User Exists

I want to create a script to check whether a user exists. I am using the logic below:
# getent passwd test > /dev/null 2&>1
# echo $?
0
# getent passwd test1 > /dev/null 2&>1
# echo $?
2
So if the user exists, then we have success, else the user does not exist. I have put above command in the bash script as below:
#!/bin/bash
getent passwd $1 > /dev/null 2&>1
if [ $? -eq 0 ]; then
echo "yes the user exists"
else
echo "No, the user does not exist"
fi
Now, my script always says that the user exists no matter what:
# sh passwd.sh test
yes the user exists
# sh passwd.sh test1
yes the user exists
# sh passwd.sh test2
yes the user exists
Why does the above condition always evaluate to be TRUE and say that the user exists?
Where am I going wrong?
UPDATE:
After reading all the responses, I found the problem in my script. The problem was the way I was redirecting getent output. So I removed all the redirection stuff and made the getent line look like this:
getent passwd $user > /dev/null
Now my script is working fine.
You can also check user by id command.
id -u name gives you the id of that user.
if the user doesn't exist, you got command return value ($?)1
And as other answers pointed out: if all you want is just to check if the user exists, use if with id directly, as if already checks for the exit code. There's no need to fiddle with strings, [, $? or $():
if id "$1" &>/dev/null; then
echo 'user found'
else
echo 'user not found'
fi
(no need to use -u as you're discarding the output anyway)
Also, if you turn this snippet into a function or script, I suggest you also set your exit code appropriately:
#!/bin/bash
user_exists(){ id "$1" &>/dev/null; } # silent, it just sets the exit code
if user_exists "$1"; code=$?; then # use the function, save the code
echo 'user found'
else
echo 'user not found' >&2 # error messages should go to stderr
fi
exit $code # set the exit code, ultimately the same set by `id`
There's no need to check the exit code explicitly. Try
if getent passwd $1 > /dev/null 2>&1; then
echo "yes the user exists"
else
echo "No, the user does not exist"
fi
If that doesn't work, there is something wrong with your getent, or you have more users defined than you think.
Why don't you simply use
grep -c '^username:' /etc/passwd
It will return 1 (since a user has max. 1 entry) if the user exists and 0 if it doesn't.
This is what I ended up doing in a Freeswitch bash startup script:
# Check if user exists
if ! id -u $FS_USER > /dev/null 2>&1; then
echo "The user does not exist; execute below commands to crate and try again:"
echo " root#sh1:~# adduser --home /usr/local/freeswitch/ --shell /bin/false --no-create-home --ingroup daemon --disabled-password --disabled-login $FS_USER"
echo " ..."
echo " root#sh1:~# chown freeswitch:daemon /usr/local/freeswitch/ -R"
exit 1
fi
By far the simplest solution:
if id -u "$user" >/dev/null 2>&1; then
echo 'user exists'
else
echo 'user missing'
fi
The >/dev/null 2>&1 can be shortened to &>/dev/null in Bash, and if you only want to know if a user does not exist:
if ! id -u "$user" >/dev/null 2>&1; then
echo 'user missing'
fi
I suggest to use id command as it tests valid user existence wrt passwd file entry which is not necessary means the same:
if [ `id -u $USER_TO_CHECK 2>/dev/null || echo -1` -ge 0 ]; then
echo FOUND
fi
Note: 0 is root uid.
I was using it in that way:
if [ $(getent passwd $user) ] ; then
echo user $user exists
else
echo user $user doesn\'t exists
fi
Script to Check whether Linux user exists or not
Script To check whether the user exists or not
#! /bin/bash
USER_NAME=bakul
cat /etc/passwd | grep ${USER_NAME} >/dev/null 2>&1
if [ $? -eq 0 ] ; then
echo "User Exists"
else
echo "User Not Found"
fi
Late answer but finger also shows more information on user
sudo apt-get finger
finger "$username"
Using sed:
username="alice"
if [ `sed -n "/^$username/p" /etc/passwd` ]
then
echo "User [$username] already exists"
else
echo "User [$username] doesn't exist"
fi
Actually I cannot reproduce the problem. The script as written in the question works fine, except for the case where $1 is empty.
However, there is a problem in the script related to redirection of stderr. Although the two forms &> and >& exist, in your case you want to use >&. You already redirected stdout, that's why the form &> does not work. You can easily verify it this way:
getent /etc/passwd username >/dev/null 2&>1
ls
You will see a file named 1 in the current directory. You want to use 2>&1 instead, or use this:
getent /etc/passwd username &>/dev/null
This also redirects stdout and stderr to /dev/null.
Warning Redirecting stderr to /dev/null might not be such a good idea. When things go wrong, you will have no clue why.
user infomation is stored in /etc/passwd, so you can use "grep 'usename' /etc/passwd" to check if the username exist.
meanwhile you can use "id" shell command, it will print the user id and group id, if the user does not exist, it will print "no such user" message.
Depending on your shell implementation (e.g. Busybox vs. grown-up) the [ operator might start a process, changing $?.
Try
getent passwd $1 > /dev/null 2&>1
RES=$?
if [ $RES -eq 0 ]; then
echo "yes the user exists"
else
echo "No, the user does not exist"
fi
Login to the server.
grep "username" /etc/passwd
This will display the user details if present.
Below is the script to check the OS distribution and create User if not exists and do nothing if user exists.
#!/bin/bash
# Detecting OS Ditribution
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$NAME
elif type lsb_release >/dev/null 2>&1; then
OS=$(lsb_release -si)
elif [ -f /etc/lsb-release ]; then
. /etc/lsb-release
OS=$DISTRIB_ID
else
OS=$(uname -s)
fi
echo "$OS"
user=$(cat /etc/passwd | egrep -e ansible | awk -F ":" '{ print $1}')
#Adding User based on The OS Distribution
if [[ $OS = *"Red Hat"* ]] || [[ $OS = *"Amazon Linux"* ]] || [[ $OS = *"CentOS"*
]] && [[ "$user" != "ansible" ]];then
sudo useradd ansible
elif [ "$OS" = Ubuntu ] && [ "$user" != "ansible" ]; then
sudo adduser --disabled-password --gecos "" ansible
else
echo "$user is already exist on $OS"
exit
fi
Create system user some_user if it doesn't exist
if [[ $(getent passwd some_user) = "" ]]; then
sudo adduser --no-create-home --force-badname --disabled-login --disabled-password --system some_user
fi
I like this nice one line solution
getent passwd username > /dev/null 2&>1 && echo yes || echo no
and in script:
#!/bin/bash
if [ "$1" != "" ]; then
getent passwd $1 > /dev/null 2&>1 && (echo yes; exit 0) || (echo no; exit 2)
else
echo "missing username"
exit -1
fi
use:
[mrfish#yoda ~]$ ./u_exists.sh root
yes
[mrfish#yoda ~]$ echo $?
0
[mrfish#yoda ~]$ ./u_exists.sh
missing username
[mrfish#yoda ~]$ echo $?
255
[mrfish#yoda ~]$ ./u_exists.sh aaa
no
[mrfish#indegy ~]$ echo $?
2
echo "$PASSWORD" | su -c "cd /" "$USER"
if [ "$?" = "0" ];then
echo "OK"
else
echo "Error"
fi
#!/bin/bash
read -p "Enter your Login Name: " loginname
home=`grep -w $loginname /etc/passwd | cut -ef:6 -d:`
if [ $home ]
echo "Exists"
else
echo "Not Exist"
fi

Resources