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.

Garrett Hall
  • 5,281

5 Answers5

74

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".)

Mat
  • 52,586
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
Michael Mrozek
  • 93,103
  • 40
  • 240
  • 233
  • So there is no way to do this with a one-liner? – Garrett Hall Nov 04 '11 at 15:16
  • @GarrettHall Why a one-liner? In any case you can write the if on one line: if [[ $(id) == 0 ]] ; then echo "Must be root to run script" ; exit; fi – Matteo Nov 04 '11 at 17:30
  • 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. – Tim Kennedy Nov 04 '11 at 17:32
  • @TimKennedy Gah, yes. Fixed – Michael Mrozek Nov 04 '11 at 17:43
  • @GarrettHall this is identical to the one liner that is in the previous answer. The only different between a one-liner, and a multi-line answer, is formatting. – Tim Kennedy Nov 04 '11 at 17:53
  • 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 Nov 04 '11 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 Nov 04 '11 at 18:25
  • Ah, yes — good catch. – janmoesen Nov 04 '11 at 18:41
  • 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. – sam hocevar Nov 04 '11 at 22:14
3

With bash:

[ $UID -ne 0 ] && echo "Must be root to run script" && exit 1
Rahul
  • 13,589
Cyrus
  • 12,309
  • 2
    That would fail to exit if echo fails (for instance because stdout is not writable). – Stéphane Chazelas Jul 25 '17 at 09:36
  • @StéphaneChazelas wouldn't that also result in a non-0 exit tho? – NSjonas Aug 26 '20 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. – Stéphane Chazelas Aug 27 '20 at 05:51
  • your response makes me realize how little bash I actually know :0 – NSjonas Aug 27 '20 at 06: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
grochmal
  • 8,657
ata
  • 802
  • 5
  • 8
  • 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: http://mywiki.wooledge.org/BashPitfalls#cmd1_.26.26_cmd2_.7C.7C_cmd3 – Flimm Nov 10 '11 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 Nov 11 '11 at 00: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 Nov 11 '11 at 00: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
  • 4
    This has already been answered and accepted. Also, your answer is near identical to what has already been posted. –  Nov 15 '16 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) – Stéphane Chazelas Jul 25 '17 at 09:47