Nathan
Nathan

Reputation: 78549

How to check if running as root in a bash script

I'm writing a script that requires root level permissions, and I want to make it so that if the script is not run as root, it simply echoes "Please run as root." and exits.

Here's some pseudocode for what I'm looking for:

if (whoami != root)
  then echo "Please run as root"

  else (do stuff)
fi

exit

How could I best (cleanly and securely) accomplish this? Thanks!

Ah, just to clarify: the (do stuff) part would involve running commands that in-and-of themselves require root. So running it as a normal user would just come up with an error. This is just meant to cleanly run a script that requires root commands, without using sudo inside the script, I'm just looking for some syntactic sugar.

Upvotes: 474

Views: 404991

Answers (25)

Sergio Abreu
Sergio Abreu

Reputation: 2909

Read official GNU Linux documentation, there are many ways to do it correctly.

Make sure you put the shell signature to avoid errors in interpretation:

#!/bin/bash

This is my script:

#!/bin/bash 

if [ $EUID -gt 0 ]; then  
  echo "Please run as root/sudo"
  exit 1
else
  #do your stuff
fi

Upvotes: 16

Theodore R. Smith
Theodore R. Smith

Reputation: 23259

Update: I have packaged this into an is_root script of my BashScripts framework. When run as a standalone command, it will echo out "Running as root". If called as a bash function, it will return true if root.


Here is the definitive answer to this question.

If using Bash, and you don't care about incompatible shells, the definitive best solution is:

SUDO=""
if (( $EUID != 0 )); then
    echo "Not running as root. Running via sudo..."
    SUDO="sudo"
fi

$SUDO ls /root

If you care about maximum portability, independent of shell, run this:

if [ -z "$EUID" ]; then
    EUID=$(id -u)
fi

if [ "$EUID" -ne 0 ]; then
    echo "Error: Not running as root/superuser."
    exit;
fi

I have just tested this on NetBSD and msys+bash on Windows, and on dash. It will work anywhere that has the id command or bash.

Upvotes: 0

Teocci
Teocci

Reputation: 8996

It is important to notice that whenever you run a script using sudo the 'user context' or environment will switch to root.

But what does that mean?

Well, my young padawan, this means that if a padawan user runs a script that contains a tilde (~) using sudo, whenever the bash will expand ~ the result will be /root and not /home/<user> (i.e., in this case /home/padawan), or if you create either a directory or a file the owner and group will be root and not the that executed the script in this case padawan, because the user environment was switched.

For instance, lets check this script install-app.sh:

#!/bin/bash
ROOT_UID=0   # Only users with $UID 0 have root privileges.
E_NOTROOT=87 # Non-root exit error.

## Prevent the execution of the script if the user has no root privileges
if [ "${UID:-$(id -u)}" -ne "$ROOT_UID" ]; then
    echo 'Error: root privileges are needed to run this script'
    exit $E_NOTROOT
fi
...
mkdir -vp ~/app/init
touch config
...
touch /home/<user>/app/init/profile
service mysql start
...

If we run using sudo:

sudo install-app.sh

This will create directories and a config file will look like this:

##
## ~ (/root)
drwxr-xr-x 17 root root    4096 Nov 23 20:45 ./
drwxr-xr-x  5 root root    4096 Nov 15 19:04 ../
...
drwxr-xr-x  3 root root    4096 Nov 25 14:30 app/
...
drwxr-xr-x  2 root root    4096 Nov 16 19:08 tmp/

## ~/app (/root/app)
drwxr-xr-x  3 root root 4096 Nov 25 14:30 ./
drwxr-xr-x 17 root root 4096 Nov 25 14:33 ../
drwxr-xr-x  2 root root 4096 Nov 25 14:33 init/

## ~/app/init (/root/app/init)
drwxr-xr-x 2 root root 4096 Nov 25 14:33 ./
drwxr-xr-x 3 root root 4096 Nov 25 14:30 ../
-rw-r--r-- 1 root root    0 Nov 25 14:33 config

## /home/<user>/app/conf
drwxr-xr-x 2 <user> <user> 4096 Nov 25 14:43 ./
drwxr-xr-x 3 <user> <user> 4096 Nov 25 14:30 ../
-rw-r--r-- 1 root   root      0 Nov 25 14:43 profile

As you can see the script is a total mess. Now the <user> cannot get access to the profile file, nor can they modify the config without using sudo. At the beginning, this may seem not important, but I assure you that if your project gets bigger, someone will run the script and mess with your system.

Recommendation:

Prompt the user to verify if they are a sudoer. Then, add sudo to the commands that require it.

The following code shows how to apply these changes to the script:

#!/bin/bash
E_NOTROOT=87 # Non-root exit error.

## Prevent the execution of the script if the user has no root privileges
## Check if is sudoer
if ! $(sudo -l &>/dev/null); then
    echo 'Error: root privileges are needed to run this script'
    exit $E_NOTROOT
fi
...
mkdir -vp ~/app/init
touch config
...
touch /home/<user>/app/init/profile
sudo service mysql start
...

This modification allows the user to run the script like this:

install-app.sh

The user will be requested to insert his password to verify if is sudoer. After,mkdir -vp ~/app/init will create the file in the user's home:

/home/<user>/app/init
/home/<user>/app/init/config
/home/<user>/app/init/profile

Also, I recommend to get the users homer directory and use it as a constant.

## Defines user home directory
USER_HOME_DIR=$(getent passwd ${SUDO_USER:-$USER} | cut -d: -f6)
...
mkdir -vp "$USER_HOME_DIR/app/init"
...

Upvotes: 4

Nathan
Nathan

Reputation: 78549

A few answers have been given, but it appears that the best method is to use is:

id -u

If run as root, this command will return an id of 0.

This appears to be more reliable than the other methods, and it seems that it return an id of 0 even if the script is run through sudo.

Here is an example of checking if running as root in a bash script (using `id -u` inline to do so):

#/bin/bash
if [ $(id -u) -ne 0 ]
  then echo Please run this script as root or using sudo!
  exit
fi
# Remainder of script goes below this line

Upvotes: 217

Alexis Dufrenoy
Alexis Dufrenoy

Reputation: 11966

I just had that problem, but I solved it in a way that could also work for another user than root. The issue being that you know that root has an ID of 0, but you won't want to use another ID, as it is arbitrary and not guaranteed to be the same if you change your server or stuff.

if [ "$(whoami)" == root] # you could put another user name here instead of root
then
  # Do stuff
else
  # Do another kind of stuff
fi

Upvotes: 0

con-f-use
con-f-use

Reputation: 4028

I don't really think UID-based answers are any good here.

There can be privileged users, that where UID != 0, there can also be systems where uid or other commands are not available.

Most of the time the best solution seems to me to just try the command without root, and then do it again with root privileges or run a command that 100% requires root and to something if it fails with a permissions error.

Upvotes: 0

Luc
Luc

Reputation: 6036

#!/usr/bin/env sh
[ "`id -u`" -ne 0 ] && echo "Please run me as root!"

Unlike all other answers, this script:

  1. Does not depend on anything being named "root", such as the user name or a /root directory.

  2. Is compatible with and tested on:

    Only "fish" is a shell that is not compatible (there seems to be no way to have compatibility between the more commons shells above and fish).

  3. Works on:

    • Linux with coreutils installed (even a minimal Debian, Ubuntu, etc. will have this)
    • FreeBSD
    • Android (based on Smeterlink's answer)

    I'd be interested if anyone has a macOS or Windows Linux Subsystem machine to test this on as well! Please comment or edit this answer.

  4. Detects:

    • sudo (tested version: 1.9.5p2)
    • doas (tested version: 6.8.1-2)
    • real root (switched user using su + password)

    Fakeroot is not detected, but if the user wants to pretend they're root, I see no reason to not let them.

Upvotes: 4

Contango
Contango

Reputation: 80398

My one-liner:

[ "$(whoami)" != root ] && echo "Please, run as root." >&2 && exit 1

Tested under Debian, Ubuntu and Docker.

Upvotes: 0

Vlastimil Buri&#225;n
Vlastimil Buri&#225;n

Reputation: 3486

In this answer, let it be clear, I presume the reader is able to understand the difference betweeen modern shells like bash, zsh and others vs portable POSIX shells like dash.

I believe there is not much to explain here since the highly voted answers do a good job of explaining much of it.

Yet, if there is anything to explain further, don't hesitate to comment, I will do my best by filling the gaps.


Optimized all-round solution for performance and reliability; all shells compatible

New solution:

# bool function to test if the user is root or not
is_user_root () { [ "${EUID:-$(id -u)}" -eq 0 ]; }

Benchmark (save to file is_user_root__benchmark)

#+------------------------------------------------------------------------------+
#|                           is_user_root() benchmark                           |
#|                  "Bash is fast while Dash is slow in this"                   |
#|                          Language: POSIX shell script                        |
#|                     Copyright: 2020-2021 Vlastimil Burian                    |
#|                      M@il: info[..]vlastimilburian[..]cz                     |
#|                               License: GPL 3.0                               |
#|                                 Version: 1.2                                 |
#+------------------------------------------------------------------------------+

readonly iterations=10000

# intentionally, the file does not have an executable bit, nor it has a shebang
# to use it, just call the file directly with your shell interpreter like:

# bash is_user_root__benchmark    ## should take a fraction of one second
# dash is_user_root__benchmark    ## could take around 10 seconds

is_user_root () { [ "${EUID:-$(id -u)}" -eq 0 ]; }

print_time   () { date +"%T.%2N"; }
print_start  () { printf '%s' 'Start  : '; print_time; }
print_finish () { printf '%s' 'Finish : '; print_time; }

printf '%s\n' '___is_user_root()___'; print_start
                   
i=1; while [ "$i" -lt "$iterations" ]; do
    is_user_root
    i=$((i+1))
done; print_finish

Examples of use and duration:

$ dash is_user_root__benchmark 
___is_user_root()___
Start  : 03:14:04.81
Finish : 03:14:13.29

$ bash is_user_root__benchmark 
___is_user_root()___
Start  : 03:16:22.90
Finish : 03:16:23.08

Explanation

Since it is multitude times faster to read the $EUID standard bash variable, the effective user ID number, than executing id -u command to POSIX-ly find the user ID, this solution combines both into a nicely packed function. If, and only if, the $EUID is for any reason not available, the id -u command will get executed, ensuring we get the proper return value no matter the circumstances.


Why I post this solution after so many years the OP has asked

Well, if I see correctly, there does seem to be a missing piece of code above.

You see, there are many variables which have to be taken into account, and one of them is combining performance and reliability.


Portable pure POSIX solution + Example of usage of the above function

#!/bin/sh

# bool function to test if the user is root or not (POSIX only)
is_user_root ()
{
    [ "$(id -u)" -eq 0 ]
}

if is_user_root; then
    echo 'You are the almighty root!'
    # You can do whatever you need...
else
    echo 'You are just an ordinary user.' >&2
    exit 1
fi

Conclusion

As much as you possibly don't like it, the Unix / Linux environment has diversified a lot. Meaning there are people who like bash, zsh, and other modern shells so much, they don't even think of portability (POSIX). It is nowadays a matter of personal choice and needs.

Upvotes: 50

Dale_Reagan
Dale_Reagan

Reputation: 2071

My one-liner:

if [ "$(id -u)" -ne 0 ]; then echo "Please run as root." >&2; exit 1; fi

Upvotes: 106

MLSC
MLSC

Reputation: 5972

Try the following code:

#!/bin/sh

if [ "$(id -u)" -ne 0 ]; then
    echo "Sorry, you are not root." >&2
    exit 1
fi

Note on edit: Backticks are considered deprecated by now (many further notes).

Upvotes: 5

WebBrother
WebBrother

Reputation: 1567

As far as I know, the correct way to check it, is as follows:

#!/bin/sh

if [ "$(id -u)" -eq 0 ]; then
    echo "You are root."
else
    echo "You are NOT root." >&2
fi

OP's Note: you may see "Testing For Root" section on linuxcommand.org.

Editor's Note: I have slightly adjusted this shell snippet to be POSIX-compatible.

Upvotes: 3

Sr. Libre
Sr. Libre

Reputation: 349

The problem using: id -u, $EUID and whoami, is all of them give false positive when I fake the root, like this:

$ fakeroot

id:

$ id -u
0

$EUID:

$ echo $EUID
0

whoami:

$ whoami
root

Then a reliable (and hacking) way is verify if the user has access to the /root directory:

$ ls /root >/dev/null 2>&1 && is_root=true || is_root=false; echo "$is_root"

Upvotes: 4

Smeterlink
Smeterlink

Reputation: 806

id -u is much better than whoami, since some systems like Android may not provide the word root.


Example command:

# whoami

Example output:

whoami: unknown uid 0

Upvotes: 3

fvillalba89
fvillalba89

Reputation: 1

My check for the root user, running in Bash:

#!/bin/bash

if [ "$UID" -eq 0 ]; then
    echo "You are root."
else
    echo "You are just an ordinary user." >&2
fi

Upvotes: -2

Pramod Kharade
Pramod Kharade

Reputation: 2085

My two cents, running in Bash:

#!/bin/bash

# Display the UID variable
echo "Your UID is $UID"

# Check for root user via UID
if [ "$UID" -eq 0 ]; then
    echo "You are root"
else
    echo "You are not root user" >&2
fi

Upvotes: -1

Slater Victoroff
Slater Victoroff

Reputation: 21934

There is a simple check for the user being root.

# Fancy red-colored `error` function with `stderr` redirection with `exit`.
error ()
{
    { printf '\E[31m'; echo "$@"; printf '\E[0m'; } >&2
    exit 1
}

# Test for root user.
if [ "$EUID" -eq 0 ]; then
    error "Do not run this as the root user"
fi

This also assumes that you want to exit with a 1 if you fail. The error function is some flair that sets output text to red (not needed, but pretty classy if you ask me).

Upvotes: 39

Matthew Trotter
Matthew Trotter

Reputation: 167

Very simple way just put:

if [ "$(whoami)" = "root" ]; then
    # you are root
else
    # you are not root
fi

The benefit of using this instead of id is that you can check whether a certain non-root user is running the command, too; eg.

if [ "$(whoami)" = "john" ]; then
    # you are john
else
    # you are not john
fi

Editor's Note: Either prefer using POSIX-compatible test command and comparison [... = ...] or you can use Bash/+other shells specific [[... == ...]].

Upvotes: 11

alexandre1985
alexandre1985

Reputation: 1116

One simple way to make the script only runnable by root is to start the script with shebang:

#!/bin/su root

Upvotes: 9

sjoerd
sjoerd

Reputation: 47

I stumbled on the same issue. The line #!/bin/su root would be an elegant method, but does not work on certain (higher) versions of bash, as mentioned by @basickarl. Further - this method will probably fail when you start the script with /bin/bash ./some_script.(b)sh.

I found an other page on SO where the use of the environment variables SUDO_UID and SUDO_USER were used. When a normal user invokes a script with sudo these are available in the script. Otherwise they are empty. Also: when you are actually logged on as root these values are empty (or unset - not entirely sure) as well.

So this is the solution I'm currently using:

isSudo()
{
  local _fn="${FUNCNAME[0]}"
  if [[ $(/usr/bin/id -u) -eq 0 ]] ; then
    if [[ ! -z "$SUDO_UID" && ! -z "$SUDO_USER" ]] ; then
      >2 echo -e "$_fn: invoked as sudo-user ($SUDO_USER)."
    else
      >2 echo -e "$_fn: invoked as root ($SUDO_USER)."
    fi
    return 0
  fi
  >2 echo "$_fn: not as root or as sudoer."
  return 1
}

Then use it like this eg.:

if $(isSudo) ; then
  echo -e "Script started as root or as sudoer."
else
  echo -e "Script not started as root or with sudo"
fi

The >2 in front of the echo is to redirect the echo to stderr otherwise the if statement goes haywire and it's just for a little debugging. In stead of using an echo, you could save "root", "sudoer" or "user" in a local variable and let the function echo that back and then perform a case on that like this:

how_invoked()
{
  local _invoked_as="USER"
  if [[ $(/usr/bin/id -u) -eq 0 ]] ; then
    if [[ ! -z "$SUDO_UID" && ! -z "$SUDO_USER" ]] ; then
      _invoked_as="SUDOER"
    else
      _invoked_as="ROOT"
    fi
  fi
  echo -n "$_invoked_as" # -n prevents newline
}

And then use it like this:

INVOKED_AS=$(how_invoked)
case $INVOKED_AS in
  USER)
    echo -e "Invoked as user." ;;
  SUDOER)
    echo -e "Invoked as sudoer." ;;
  ROOT)
    echo -e "Invoked as root." ;;
  *)
    echo -e "I'm confused" ;;
esac

Or if you just want to test for one of those:

if [ "$(how_invoked)" == "SUDOER" ] ; then
  echo -e "You are a sudoer."
else
  echo -e "You are NOT a sudoer."
fi

Hope this helps.

Upvotes: 0

Mike Q
Mike Q

Reputation: 7337

Check if you are root and quit if you are not:

if ((EUID != 0)); then
    echo "Root or Sudo  Required for script ( $(basename $0) )"
    exit
fi

Or in this example, try to create a directory in root location then try after rights were elevated.

Check if you are root and if not elevate if possible :

# Fails to create these dirs (needs sudo)
mkdir /test-dir-$(basename $0)
rmdir /test-dir-$(basename $0)

if ((EUID != 0)); then
    echo "Granting root privileges for script ( $(basename $0) )"
    if [[ -t 1 ]]; then
        sudo "$0" "$@"
    else
        exec 1> output_file
        gksu "$0" "$@"
    fi
    exit
fi
echo "Root privileges granted..."
# Creates Dirs as it now has rights
mkdir /test-dir-$(basename $0)
rmdir /test-dir-$(basename $0)

Upvotes: 1

ptierno
ptierno

Reputation: 10064

The $EUID environment variable holds the current user's UID. Root's UID is 0. Use something like this in your script:

if [ "$EUID" -ne 0 ]
  then echo "Please run as root"
  exit
fi

Note: If you get 2: [: Illegal number: check if you have #!/bin/sh at the top and change it to #!/bin/bash.

Upvotes: 669

sberder
sberder

Reputation: 4645

In a bash script, you have several ways to check if the running user is root.

As a warning, do not check if a user is root by using the root username. Nothing guarantees that the user with ID 0 is called root. It's a very strong convention that is broadly followed but anybody could rename the superuser another name.

I think the best way when using bash is to use $EUID, from the man page:

EUID   Expands to the effective user ID of the current  user,  initialized
       at shell startup.  This variable is readonly.

This is a better way than $UID which could be changed and not reflect the real user running the script.

if (( $EUID != 0 )); then
    echo "Please run as root"
    exit
fi

A way I approach that kind of problem is by injecting sudo in my commands when not run as root. Here is an example:

SUDO=''
if (( $EUID != 0 )); then
    SUDO='sudo'
fi
$SUDO a_command

This ways my command is run by root when using the superuser or by sudo when run by a regular user.

If your script is always to be run by root, simply set the rights accordingly (0500).

Upvotes: 106

Jeremy J Starcher
Jeremy J Starcher

Reputation: 23873

As @wrikken mentioned in his comments, id -u is a much better check for root.

In addition, with proper use of sudo, you could have the script check and see if it is running as root. If not, have it recall itself via sudo and then run with root permissions.

Depending on what the script does, another option may be to set up a sudo entry for whatever specialized commands the script may need.

Upvotes: 30

John Kugelman
John Kugelman

Reputation: 362137

If the script really requires root access then its file permissions should reflect that. Having a root script executable by non-root users would be a red flag. I encourage you not to control access with an if check.

chown root:root script.sh
chmod u=rwx,go=r script.sh

Upvotes: 5

Related Questions