66

I'm writing a bash script where I want to exit if the user is not root. The conditional works fine, but the script does not exit.

[[ `id -u` == 0 ]] || (echo "Must be root to run script"; exit)

I've tried using && instead of ; but neither work.

5 Answers 5

75

You could do that this way:

[[ $(id -u) -eq 0 ]] || { echo >&2 "Must be root to run script"; exit 1; }

("ordinary" conditional expression with an arithmetic binary operator in the first statement), or:

(( $(id -u) == 0 )) || { echo >&2 "Must be root to run script"; exit 1; }

(arithmetic evaluation for the first test).

Notice the change () -> {} - the curly brackets do not spawn a subshell. (Search man bash for "subshell".)

6
  • 1
    Please exit with a code other than zero, example: exit 1 in order to make understand the parent process that a problem occured.
    – SamK
    Commented Nov 4, 2011 at 16:32
  • 1
    You shouldn't use [[ for numeric comparison, use ((.
    – Chris Down
    Commented Nov 4, 2011 at 19:59
  • 2
    @ChrisDown [[ is fine, as long as you use -eq instead of ==. Commented Nov 24, 2011 at 10:10
  • Fixed the conditional, added the arithmetic version, @ChrisDown.
    – Mat
    Commented Nov 24, 2011 at 10:27
  • 2
    @Mat By the way, you can shorten it to (( EUID )) && ...
    – Chris Down
    Commented Nov 24, 2011 at 11:14
27

The parentheses around those commands creates a subshell. Your subshell echos "Must be root to run script" and then you tell the subshell to exit (although it would've already, since there were no more commands). The easiest way to fix it is probably to just use an if:

if [[ `id -u` != 0 ]]; then
    echo "Must be root to run script"
    exit
fi
9
  • So there is no way to do this with a one-liner? Commented Nov 4, 2011 at 15:16
  • 1
    your logic is backwards. in your example, if id -u == 0, which would mean that you are root. You want [[ $(id -u) != 0 ]]; then. Commented Nov 4, 2011 at 17:32
  • 4
    If you /must/ have a one-liner, try this on for size: [ "$UID" != 0 ] && echo 'You have to be root.' && exit 1; Also note the $UID, which saves spawning a process. I think you might even prefer $EUID.
    – janmoesen
    Commented Nov 4, 2011 at 18:19
  • 1
    @janmoesen, good point. And while wee have a variable with a numeric value: ((UID)) && echo 'You have to be root.' && exit 1.
    – manatwork
    Commented Nov 4, 2011 at 18:25
  • 3
    @janmoesen: note that using such inverse logic will cause a script that set -e to abort. One solution to that problem is [ "$UID" != 0 ] && echo 'You have to be root.' && exit 1 || true. Commented Nov 4, 2011 at 22:14
3

With bash:

[ $UID -ne 0 ] && echo "Must be root to run script" && exit 1
4
  • 2
    That would fail to exit if echo fails (for instance because stdout is not writable). Commented Jul 25, 2017 at 9:36
  • @StéphaneChazelas wouldn't that also result in a non-0 exit tho?
    – NSjonas
    Commented Aug 26, 2020 at 22:24
  • 1
    @NSjonas, echo (contrary to eval, exec, :...) is not a special builtin (it doesn't even have to be a builtin), so its failure wouldn't cause the shell to exit. Try bash -c 'echo >&-; echo "$?"'. bash's special builtin only cause the shell to exit when it's in POSIX compliant mode. Commented Aug 27, 2020 at 5:51
  • your response makes me realize how little bash I actually know :0
    – NSjonas
    Commented Aug 27, 2020 at 6:12
1

Brackets around || and && are not required as these are right-associative. The following two expressions are equivalent:

expr1 || expr2 && expr3
expr1 || { expr2 && expr3 }

So && instead of ; would work just fine, as echo will return true.

[[ $(id -u) == 0 ]] || echo "Must be root to run script" && exit 1
3
  • 2
    While everything you said is correct, this is a bad pattern to learn, because you're relying on the return value of expr2. Are you certain that echo will always return a 0 exit status? It's much better idea to group the statements with curly braces and semi-colons. This is such a common pitfall that it has its own entry in BashPitfalls: mywiki.wooledge.org/BashPitfalls#cmd1_.26.26_cmd2_.7C.7C_cmd3
    – Flimm
    Commented Nov 10, 2011 at 23:10
  • 1
    @Flimm: I don't agree it's a bad pattern. Evidently, it use is case-dependant, and also depends on your knowledge of the return values you'll get. In this case I'm sure echo will return 0 the 99.999% of times, and if it stomps on a write error (the only case it won't return 0) there's a bigger problem than this expr. There's also the case where YOU are generating the return values, so no, it isn't a "bad pattern" per se for me.
    – ata
    Commented Nov 11, 2011 at 0:18
  • I'll add too, like it says in the wiki, that you should use it if sure to understand C evaluation. Anyway, a little testing should clear any ambiguities.
    – ata
    Commented Nov 11, 2011 at 0:26
0

this might help you , in bash

[oracle@rac1 ~]$ which bash
/bin/bash
[oracle@rac1 ~]$ cat test1.sh
if [ `id -u` != 0 ]
then
echo "Must be root to run the script
 "
exit
fi
2
  • 4
    This has already been answered and accepted. Also, your answer is near identical to what has already been posted.
    – anon
    Commented Nov 15, 2016 at 10:12
  • 1
    @maulinglawns, that answer, contrary to the other ones has the merit of being portable to all Bourne-like shells (it would be better if the error was output on stderr, the exit status was non-zero and the command substitution was quoted though) Commented Jul 25, 2017 at 9:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .