169

I'm writing a simple bash script, but I need it to check whether it's being run as root or not. I know there's probably a very simple way to do that, but I have no idea how.

Just to be clear:
What's a simple way to write a script foo.sh, so that the command ./foo.sh outputs 0, and the command sudo ./foo.sh outputs 1?

Malabarba
  • 9,956
  • 11
  • 41
  • 45

11 Answers11

227
#!/bin/bash
if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root" 
   exit 1
fi
aneeshep
  • 29,975
  • 14
  • 64
  • 77
  • 7
    I'm accepting this one cause (after some testing) using the system variable EUID turned out to be (approx 100 times) faster than running a command (id -u), but all of the answers worked great. – Malabarba Dec 02 '10 at 11:59
  • Thanks aneeshep. I didn't even know there was an environment variable for the effective UID. – Delan Azabani Dec 02 '10 at 12:31
  • Be careful though! A program can *temporarily* drop its superuser privileges in which case `$UID` and `id -ru` are still 0, but `$EUID` and `id -u` aren't. See [`getuid(2)`](http://linux.die.net/man/2/getuid) and decide what you need. – David Foerster Oct 16 '14 at 09:26
  • 23
    Also, `$UID` and `$EUID` are bashisms. Merely POSIX compliant shells don't have those variables and you need to use [`id(1)`](http://linux.die.net/man/1/id) instead. – David Foerster Oct 16 '14 at 09:28
  • 4
    in a bash script, you could use `if ((EUID)); then`, see [@geirha's comment](http://askubuntu.com/questions/15853/how-can-a-script-check-if-its-being-run-as-root#comment33736_30161) – jfs Oct 17 '14 at 19:38
  • 1
    -1. This does not meet the requirements of the question. Running `echo $EUID` as the first 'normal' user gives me `1000` (expected). Running `sudo echo $EUID` gives me `1000` (not expected). On the other hand, `id -u` gives me `1000`, and `sudo id -u` gives me `0` (both expected). – Andrew Ensley Oct 16 '15 at 15:17
  • 9
    @Andrew: Variables are substituted before they are passed to `sudo`. If you run `sudo sh -c 'echo $EUID'` you will see your expected results. – sourcenouveau Nov 19 '15 at 18:09
  • @M.Dudley That makes sense. I would take back my downvote if I could. However, `sudo sh -c 'echo $EUID'` gives me a blank line. I should see `0`. – Andrew Ensley Nov 19 '15 at 18:12
  • 8
    @Andrew – I know I'm way late here, but for posterity: `$EUID` is a Bashism (as mentioned above by @DavidFoerster), and your `/bin/sh` is most likely a link to `/bin/dash`, not `/bin/bash`. `sudo bash -c 'echo $EUID'` will yield the expected result. – Adrian Günter May 09 '17 at 21:40
  • @AdrianGünter Even if it's a link to `/bin/bash`, many (all?) Bash installs will run in POSIX mode if called as `sh`. That is, they'll intentionally ignore Bashisms. It's useful as a first-pass test if you're trying to write POSIX-compliant shell scripts, but can cause issues if you're using `sh -c` to delay interpolation. – Nic Jun 12 '19 at 17:30
  • Can also be used as one-liner like `[[ $EUID -ne 0 ]] && echo "non-root" || echo "root"` – fcole90 Aug 28 '21 at 08:54
152

A root user does not have to be named "root". whoami returns the first username with user ID 0. $USER contains the name of the logged in user, which can have user ID 0, but have a different name.

The only reliable program to check whether the account is logged in as root, or not:

id -u

I use -u for the effective user ID, not -r for the real user ID. Permissions are determined by the effective user ID, not the real one.

Tests

/etc/passwd contains the following usernames with user ID 0 in the given order:

rootx
root2

Logged in as root2, gives the next results:

  • whoami: rootx
  • echo $USER: root2 (this returns an empty string if the program was started in an empty environment, e.g. env -i sh -c 'echo $USER')
  • id -u: 0 As you can see, the other programs failed in this check, only id -u passed.

The updated script would looks like this:

#!/bin/bash
if ! [ $(id -u) = 0 ]; then
   echo "I am not root!"
   exit 1
fi
Lekensteyn
  • 171,743
  • 65
  • 311
  • 401
  • 5
    I always try to stay as POSIX compliant as possible, using `sh` (`dash`) as interpreter instead of `bash`. But good shot, saves another fork :) – Lekensteyn Mar 13 '11 at 10:34
  • After being out cold for a while, i came back and took a look. Lekensteyn's method does indeed seem to be better. His answer is now the new accepted answer. +1 to you, Lekensteyn, esp. for getting that hard-to-get badge off of this question ;) – Thomas Ward Mar 13 '11 at 18:13
  • Thanks for waiting that long for accepting my question :D I got a Gold Populist badge and a bronze Nice Answer for this answer :) I wonder how this question got that popular, 345 views in a day! – Lekensteyn Mar 13 '11 at 21:10
  • I still love using this test: `[ -w /etc/shadow ]`. If that's true, you might as well be root. ;) – Kees Cook Mar 14 '11 at 22:44
  • 3
    I've seen shorter versions, `[ -w / ]`. The idea remains the same. Note: in certain chroots, `[ -w /etc/shadow ]` will fail because `/etc/shadow` is non-existent, therefore the approach with `/` is preferred. A way better would checking the actual need for root permissions. If the script needs to write to `/etc/someconfig`, just check if that file is writable OR the file does not exist and `/etc` is writable. – Lekensteyn Mar 14 '11 at 22:47
  • Yes, this is a great answer – César Feb 14 '12 at 05:01
40

As @Lekensteyn said you should use effective user ID. You don't need to call id -u in bash:

#!/bin/bash

if [[ $EUID -ne 0 ]]; then
   echo "You must be root to do this." 1>&2
   exit 100
fi

@geirha's suggestion from the comments uses arithmetic evaluation:

#!/bin/bash

if (( EUID != 0 )); then
   echo "You must be root to do this." 1>&2
   exit 100
fi
jfs
  • 3,948
  • 1
  • 32
  • 46
  • 9
    In general one should use `((..))` for testing numbers, and `[[..]]` for testing strings and files, so I'd do `if (( EUID != 0 )); then` instead, or just `if ((EUID)); then` – geirha Mar 13 '11 at 12:41
  • @geirha: I've added your suggestion. – jfs Mar 13 '11 at 13:11
  • 2
    `EUID` should become `$EUID`. Instead of using `(( $EUID != 0 ))`, use `[ $EUID != 0 ]`. It will work with `dash` too. – Lekensteyn Mar 13 '11 at 13:23
  • 2
    @Lekensteyn: `EUID` works just fine. The question is tagged `bash` not `dash`. The command `env -i sh -c '[ $EUID != 0 ]'` fails, but `env -i bash -c '(( EUID != 0 ))'` works. btw, `dash` doesn't work for some non-ascii characters on Ubuntu http://stackoverflow.com/questions/5160125/how-to-create-a-non-ascii-file-using-redirection-with-sh-c It is 2011 and there are people who use other languages. – jfs Mar 13 '11 at 13:39
  • After looking back on this while being horribly bored, and testing, I am going to use this method as listed here with the `$EUID` stuff from this point forward. I have changed the accepted answer as a result. – Thomas Ward Aug 03 '11 at 15:15
  • None of these examples work for me on 12.04. I either get `EUID: not found` or `[[: not found`. `[ $(id -u) = 0 ]` worked like a charm though. – blee Aug 29 '12 at 19:12
  • @briankb: the examples work. Make sure you use bash, not dash. – jfs Aug 29 '12 at 21:25
22

You can accomplish this by using the whoami command, which returns the current user:

#!/bin/bash

if [ `whoami` != 'root' ]
  then
    echo "You must be root to do this."
    exit
fi

...

Running the above will print You must be root to do this. if the current user is not root.


Note: an alternative in some cases is to simply check the $USER variable:

if [ $USER != 'root' ]
Nathan Osman
  • 31,915
  • 40
  • 179
  • 259
  • I'll take a look at the code, see if it doesn’t make the script fail or something. If it works, I'll accept your answer :) – Thomas Ward Mar 13 '11 at 07:40
  • Put this into my scripts, it worked beautifully. Thanks much. :) – Thomas Ward Mar 13 '11 at 07:42
  • 4
    @George Edison: I recommend against using `$USER`, especially in this way. `$USER` is set by the login shell, it does not necessary propagate to the program (`env -i sh -c 'echo $USER'`). In that way, `$USER` is empty and an syntax error occurs. – Lekensteyn Mar 13 '11 at 09:26
  • 2
    @Lekensteyn Not only is `$USER` set by the shell, it is also something that is easy to spoof. Whoami will return the actual user. – Paul de Vrieze Mar 13 '11 at 11:48
  • 2
    @PauldeVrieze What's the point of fooling that check? – htorque Mar 13 '11 at 14:01
  • 2
    `id -u` is a far superior solution. I believe this answer is potentially misleading and should not have been accepted. – sam hocevar Mar 13 '11 at 16:05
  • `andrew@andrew-testing:~$ sudo whoami` `root` `andrew@andrew-testing:~$ sudo echo $USER` `andrew` – andrewsomething Mar 13 '11 at 16:22
  • @Sam: How is it misleading? – Nathan Osman Mar 13 '11 at 19:21
  • 2
    @andrewsomething: `$USER` is interpreted by your shell, your example should be `sudo sh -c 'echo $USER'` in order to be meaningful. @George: it makes the wrong assumption that `whoami` will always return `root` for UID 0. – sam hocevar Mar 13 '11 at 20:26
13

Taking efficiency into consideration, you may test, first, the EUID environment variable and then, if it doesn't exist, call the standard id command:

if ((${EUID:-0} || "$(id -u)")); then
  echo You are not root.
else
  echo Hello, root.
fi

This way, because of the OR shortcut, you avoid calling a system command, prioritizing the query of an in-memory variable.

Code reuse:

function amIRoot() {
  ! ((${EUID:-0} || "$(id -u)"))
}
Luchostein
  • 254
  • 2
  • 8
  • 1
    I'm afraid you haven't benchmarked it, well I have and this takes the very same time as `id -u`. See the bench script along with results below it here: https://pastebin.com/srC3LWQy – Vlastimil Burián Oct 16 '19 at 23:40
  • 3
    *`((${EUID:-0} || "$(id -u)"))` always runs `id -u`*, just as `((1 || $(echo hi >&2; echo 1)))` and `((0 || $(echo hi >&2; echo 1)))` both print `hi`. *Outside* shell arithmetic, `&&` and `||` are control operators; in `a || "$(b)"`, `b` only runs if `"$(b)"` runs. Most expansions, including command substitution, are governed by control operators' short-circuiting semantics. Shell arithmetic has operators that *happen* to be spelled `&&` and `||`, too--which also short-circuit in that `((1 || 0/0))` is not an error--but shell arithmetic occurs *after* nested command substitutions are expanded. – Eliah Kagan Oct 24 '19 at 18:02
10
#!/bin/bash
[[ $(id -u) != 0 ]] 
echo $?
João Pinto
  • 17,029
  • 5
  • 55
  • 68
  • 1
    Numeric comparison is better done with `-ne` (counter-intuitive: text operands are for numeric comparison) – Gonen Aug 28 '22 at 20:53
3
#!/bin/bash
if [ "$(id -u)" == "0" ]
then
    echo "I am root."
else
    echo "I am not root."
fi
Teemu Leisti
  • 284
  • 2
  • 16
Delan Azabani
  • 434
  • 3
  • 9
2

This snippet would:

  • Check on different sessions
  • suggest to use sudo !!
  • and return an error
if [ "$(whoami &2>/dev/null)" != "root" ] && [ "$(id -un &2>/dev/null)" != "root" ]
      then
      echo "You must be root to run this script!"
      echo "use 'sudo !!'"
      exit 1
fi
rubo77
  • 31,573
  • 49
  • 159
  • 281
2

This answer is just to save an idea with may be helpful for some of you. If you need script which is run from desktop GUI and requires root privileges try this way:

#!/bin/bash
if ! [ $(id -u) = 0 ]; then
   gksudo -w $0 $@
   exit
fi

#here go superuser commands, e.g. (switch intel cpu into powersave mode):
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
cpupower frequency-set -g powersave

This way you will get nice dialog window asking you for root user password. Just like with command-line sudo.

The gksudo may not be available in your system then install it with sudo apt-get install gksu.

gertas
  • 139
  • 3
2

One simple way to make the script only runnable by root is to start the script with the line:
#!/bin/su root

  • 3
    Don't do this. It attempts to log in directly as root, whereas sudo may be required on many systems because the former is forbidden. Furthermore, the shebang is intended to ensure your script runs in the one for which it was designed, whereas your method will cause the script to run in root's default shell, which cannot be predicted if the script is run by anyone other than the author. Therefore, using the shebang to elevate privileges of a script will severely break its portability and result in errors on many systems. (su: Authentication failure) – StarCrashr Sep 20 '18 at 01:59
  • 1
    Don't do this. Some joker might create a second user named `root`, or make a user named `root` that isn't `root`, thereby fooling your program into thinking it's `root`. See https://askubuntu.com/a/836621/1070384. – Sapphire_Brick Aug 18 '20 at 22:21
1

This code works under both Bash and stock Bourne sh (tested under FreeBSD) that does not define EUID. If EUID exists its value is returned, else the 'id' command is run. It is a tad shorter than the "or" example above, as it uses a shell built-in ":-".

Root in sh:

# echo $EUID

# euid=${EUID:-`id -u`}
# echo $euid
0

In bash:
[chaapala@tenfwd2 ese]$ euid=${EUID:-`id -u`}
[chaapala@tenfwd2 ese]$ echo $euid
10260