Linux Basic — Checking for Root
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:
$EUIDis 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:
whoamiforks 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
whoamiby 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:
execreplaces the current shell withsudo …— no new process--preserve-env=PATHkeeps your$PATH(otherwisesudoresets it)"$0" "$@"re-runs the same script with the same arguments- If the user can’t
sudo, the call fails and the script ends withsudo’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