Score:3

Shell script keeps executing even if was true

in flag

I have a simple shell script to check if the user exist, if not, add that user

#!/usr/bin/bash
echo "Enter user"
read -r user
if [ "$(id -u "$user" &>/dev/null)" = 1 ]; then
  echo "user existed"
else
  useradd "$user"
  echo "add password"
  passwd "$user"
  echo "$user" &>>text.txt
  echo "saved"
fi

But it does not display the "echo user exist". instead it displays like so:

Enter user

abcdef

useradd: user 'abcdef' already exists

add password

New password: 

In this case abcdef is an existing user

Please let me know what goes wrong here, thanks a lot.

MatsK avatar
mp flag
A tip to troubleshoot, run the command `id -u testuser` and check if the result is 1. Because 1 is the only thing that will proceed to `echo "User exists"`
raj avatar
cn flag
raj
@MatsK Even 1 won't proceed, as the output from `id` is redirected to `/dev/null`.
Terrance avatar
id flag
After your check of `id -u $user &>/dev/null` use `echo $?` and that will produce either a 0 or 1. 0 meaning `exit code 0` or true, 1 meaning `exit code 1` or false. Or you can have `if [[ $? == 0 ]]; then`, etc.
raj avatar
cn flag
raj
@Terrance It's simpler just to use `id -u $user &>/dev/null` straight away as condition in `if`.
Terrance avatar
id flag
@raj not in every case. Actually doing it with `case` statements works very well. The `id -u` produces the user ID number and not a 1 or 0. So, outputting `id -u` to >/dev/null will get rid of the 1000 if that is the user ID.
raj avatar
cn flag
raj
@Terrance Look at my answer below. Using the command directly as a condition to `if` (without unnecessary `$(...)`) checks it's exit code, not the output.
Terrance avatar
id flag
@raj That will work too. Mine is just another way.
Score:9
cn flag
raj

The following condition in your script:

if [ "$(id -u "$user" &>/dev/null)" = 1 ]; then

will never be true. It would be true only if the output of command id -u "$user" &>/dev/null would be the single digit 1, but as any output from that command is redirected to /dev/null, this will never happen.

Replace that line with

if { id -u "$user" &>/dev/null ; } then

This condition checks the exit status of the command (and not its output like $(...) does) and will be true if the command id -u "$user" &>/dev/null succeeds (that is, user exists) and false if it fails (that is, user doesn't exist).

in flag
Thank for your answer, it work!!v But what different between [] and {}?
raj avatar
cn flag
raj
`[` is a **command** (equivalent to `test`; see `man [`) used to check the result of various conditional expressions. `{}` are just parentheses to enclose the command. Actually, we may skip `{}` altogether (while you **cannot** skip `[]`): `if id -u "$user" &>/dev/null ; then` will work as well only is less readable.
in flag
i sitll dont understand `[` is a command to check the value, then, in my command '"$(id -u "$user" &>/dev/null)" = 1` why it not return true, i mean, `"$(id -u "$user" &>/dev/null)"` will return 0 or 1 right?
raj avatar
cn flag
raj
The value of `$(command)` is the **output** of the command, ie. the text the command prints. Because you redirect that output to `/dev/null`, the command prints nothing. So the value of `$(id -u "$user" &>/dev/null)` will be always **an empty string**. You compare that empty string to the digit 1, which can never return true.
in flag
The `{` and `}` are superfluous. `if id -u "$user" &>/dev/null; then` is enough.
raj avatar
cn flag
raj
@JanHudec I know, but the code is more clear with `{}`. They are ther just for style :)
Score:2
id flag

The answer by @raj is 100% correct for simplifying code.

These are just some alternate ways of using the exit codes and maybe shedding a little more light in how they can be used.

The exit code can be checked by doing an echo $? right after your command of id -u $user &>/dev/null which doesn't need to be encased in $(..) or anything like that.

Just try id -u <username> &>/dev/null and then type in echo $? to check the exit code.

Example:

terrance@terrance-ubuntu:~$ id -u terrance &>/dev/null
terrance@terrance-ubuntu:~$ echo $?
0
terrance@terrance-ubuntu:~$ id -u mark &>/dev/null
terrance@terrance-ubuntu:~$ echo $?
1

In the above example, the user mark does not exist on my computer thus the exit status of 1.

Using the exit code you can put it into a case statement instead of an if..else statement.

#!/bin/bash
echo "Enter username"
read -r user
id -u "$user" &>/dev/null
case $? in
    0) echo "User exists";;
    1) adduser $user
       echo "add password"
       passwd $user
       echo $user >> text.txt
       echo "saved";;
esac

Interestingly enough, using an if [ ]; then can still be used as well by adding an echo $? into your if check, but I did notice that you were checking for 1 to be if exist, but 1 means that the code exited with 1 meaning not found.

Adding it will work like this as well.

#!/bin/bash
echo "Enter username"
read -r user
if [ $(id -u "$user" &>/dev/null; echo $?) == 0 ]; then
    echo "User exists"
else
    adduser $user
    echo "add password"
    passwd $user
    echo $user >> text.txt
    echo "saved"
fi
in flag
Thank, i learn a lot from your answer, but i dont understand why `echo $` ,what is that ??
Terrance avatar
id flag
@LêQuốcKhánh Maybe this here might shed a little more light on the `echo $?` for checking exit commands: https://askubuntu.com/questions/646526/what-is-is-it-a-variable
Terrance avatar
id flag
@LêQuốcKhánh Also, when made the check as `id -u "$user" &>/dev/null; echo $?` it is now 2 commands as 1.
in flag
It's a life-hack for shell bash =) ,thank you a lots
Terrance avatar
id flag
@LêQuốcKhánh You are very welcome!
in flag
Can i ask you another ques? I know `&>` will move stdout and stderr to , but in this case why do we need move these value to `/dev/null` , all i understand now is `id -u "$user" &>/dev/null` return 0 or 1, then move to `/dev/null` But all we need is check check is was 0 or 1, but why we need move to `/dev/null` , if i use only ` id -u "$user" &>/dev/null` , what happend? Actually this code from our website and not totally understand
Terrance avatar
id flag
@LêQuốcKhánh Using `/dev/null` is a way to basically erase the stdout from you seeing it on the screen. It is just a way to keep the stuff clean. You can get the same exit code or status without using it, but you will see all that output to the screen and it might make it more difficult to use your code. Feel free to test your code without the `&>/dev/null` as it should still work.
Terrance avatar
id flag
@LêQuốcKhánh The 0 and 1 is only the exit status. If you run `id -u $user` without anything after it, it will produce the user ID number. Like my username produces 1000 as that is my user ID number. But, if I type in `echo $?` after that command it will still produce a 0 since the exit status passed.
in flag
thank you so much
in flag
I just try to log by `user1="$(id -u "$user")"` and `echo "User is $user1"` and it return 1007, what that mean?
Terrance avatar
id flag
@LêQuốcKhánh `id -u` produces the UID or the user ID number not the exit status. You have to run `echo $?` as the next command to see what the exit status of the command `id -u` is.
in flag
I understand now, thank you a lot
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.