Linux Basic — Checking for Root

3 minute read

Checking for Root

Many scripts need root privileges — to install packages, edit /etc, restart services, or modify system files. Checking for root upfront gives a clear error instead of a confusing Permission denied 200 lines later, and lets you optionally re-execute the script under sudo.


The Idiomatic Root Check — $EUID

$EUID is the effective user ID — what the OS will actually permit. Root is always 0:

if [[ $EUID -ne 0 ]]; then
    echo "This script must be run as root." >&2
    exit 1
fi

That’s the gold standard:

  • $EUID is a Bash builtin variable — no fork, no external command
  • Works under sudo, under SELinux, under containers, everywhere
  • Numeric comparison (-ne 0) is unambiguous

For POSIX sh (no $EUID), use id -u:

if [ "$(id -u)" -ne 0 ]; then
    echo "Must be root" >&2
    exit 1
fi

Why Not whoami?

# ❌ Fragile
if [[ "$(whoami)" != "root" ]]; then ...

Problems:

  • whoami forks a process every call (slow in loops)
  • Returns the login name, not the numeric UID — UID 0 might be named something else on hardened systems
  • Some distros don’t ship whoami by default (BusyBox)

Use $EUID (Bash) or id -u (POSIX). Never whoami for permission checks.


Self-Re-Exec Under sudo

A friendly script can prompt for sudo when needed instead of just failing:

if [[ $EUID -ne 0 ]]; then
    echo "Re-running as root..."
    exec sudo --preserve-env=PATH -- "$0" "$@"
fi

# From here on, $EUID -eq 0

What’s happening:

  • exec replaces the current shell with sudo … — no new process
  • --preserve-env=PATH keeps your $PATH (otherwise sudo resets it)
  • "$0" "$@" re-runs the same script with the same arguments
  • If the user can’t sudo, the call fails and the script ends with sudo’s exit code

A more conservative version that prompts before escalating:

if [[ $EUID -ne 0 ]]; then
    read -p "Need root. Re-run with sudo? [y/N] " yn
    [[ "${yn,,}" == y* ]] || exit 1
    exec sudo -- "$0" "$@"
fi

EUID vs UID — Why “Effective” Matters

Variable Meaning
$UID Real user ID (who launched the process)
$EUID Effective user ID (what the OS uses for permission checks)

These differ when a binary has the setuid bit:

ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 /usr/bin/passwd     ← note the 's' for setuid

Running passwd as a normal user: $UID is your user, $EUID is root. The OS only cares about $EUID, so that’s what you should check.


Dropping Privileges — sudo -u

Sometimes the opposite is true: your script runs as root (from cron, systemd, etc.) and you want to drop to a specific user for one step:

# We are root. Run the user-data command as 'webuser'
sudo -u webuser /usr/local/bin/sync-cache --once

Useful flags:

Flag Meaning
-u USER become USER
-H also set $HOME to that user’s home dir
-i login-style — clean env, cd to home, run their shell
-E preserve current env (use sparingly)
# Run a backup as 'backup' with their full env
sudo -iu backup /opt/scripts/run-backup.sh

For a tight per-step privilege drop in a long-running root script, a helper is handy:

as_user() {
    local user=$1; shift
    sudo -u "$user" -H -- "$@"
}

as_user webuser /usr/local/bin/sync-cache --once
as_user www-data tail -n 1 /var/log/nginx/access.log

Detecting Whether We Were sudo-Invoked

sudo sets $SUDO_USER to the original user. That can be useful for messages or for restoring ownership at the end:

if [[ -n "${SUDO_USER:-}" ]]; then
    echo "Originally invoked by $SUDO_USER"
fi

# After creating files as root, chown them back to the original user
chown -R "${SUDO_USER:-root}:${SUDO_USER:-root}" /home/"$SUDO_USER"/.myapp/

Putting It All Together — Boilerplate

A copy-paste-ready header that handles root requirements gracefully:

#!/usr/bin/env bash
set -euo pipefail

# Re-exec under sudo if not root
if [[ $EUID -ne 0 ]]; then
    exec sudo --preserve-env=PATH -- "$0" "$@"
fi

# Identify original user (for ownership / logging)
INVOKED_BY=${SUDO_USER:-$USER}
echo "Running privileged operations on behalf of $INVOKED_BY"

# ... rest of script ...

Common Mistakes

❌ Wrong ✅ Right Issue
if [ "$(whoami)" = "root" ] if [[ $EUID -eq 0 ]] forks + name lookup
if [ "$UID" -eq 0 ] if [[ $EUID -eq 0 ]] UID ignores setuid
sudo ./script.sh (every time) exec sudo -- "$0" "$@" once at top duplicate prompts
sudo cmd1; sudo cmd2; sudo cmd3 single sudo bash -c '...' or run script as root each sudo re-prompts
Forgetting $SUDO_USER for created files chown "$SUDO_USER:$SUDO_USER" file leaves user with root-owned files in their $HOME

Next → Directory Operations

Updated: