How To Run A Script In Linux

How To Run A Script In Linux

Reading time1 min
#Linux#Scripting#Shell#ScriptExecution#Bash#Automation

Mastering Script Execution in Linux: Pitfalls, Best Practices, and Real-World Nuances

Running scripts in Linux is less about the mechanics of chmod +x and more about eliminating ambiguity, enforcing correct environments, and controlling side effects. Overlook these nuances and sooner or later you'll encounter subtle breakages, privilege escalations, or deployment regressions.


Minimal Execution: Permissions and Launch

Start with the classic:

chmod +x myscript.sh
./myscript.sh
  • chmod +x sets the executable bit.
  • ./ forces the shell to look in the current directory.
    Note: Typing myscript.sh alone (without ./) will usually fail unless . is incorrectly in your PATH—a mistake that exposes the system to serious attack vectors.

Interpreter Consistency: The Shebang Is Not Optional

At the top of every portable script:

#!/bin/bash

or

#!/usr/bin/env python3

Without a proper shebang, expect behavior to drift—especially across distributions (e.g., dash as default /bin/sh on Debian/Ubuntu, bash elsewhere).
Example: a script that uses [[ ... ]] runs in Bash but explodes in Dash.

Gotcha:

dash: 1: [[: not found

Always verify the shebang path exists:

which bash

Avoid Polluting PATH with '.'

Security trade-off:
Adding the current directory . to $PATH (e.g., export PATH=.:$PATH) is hazardous. A user could unknowingly execute a malicious ls, cat, or sudo dropped into their working directory.
Best practice: never add . to $PATH—make script execution explicit.


Alternate: Direct Interpreter Execution

Scripts don’t require executable permission if invoked via interpreter:

bash myscript.sh
python3 myscript.py

Useful when working in shared or read-only environments (e.g., CI runners, mounted NFS volumes) where modifying file modes isn’t possible. Also applies for shebang-incompatible interpreters or legacy scripts needing sh or env workarounds.


Environment Isolation Is Non-trivial

Execution context matters:

  • Interactive shell: inherits user environment, expanded $PATH, often sourcing ~/.bashrc.
  • Non-interactive (cron, systemd, SSH forced commands): almost no environment loaded.

Cron classic:

* * * * * /home/user/myscript.sh

Debug: Add env > /tmp/cronenv.txt in the script to reveal a gutted environment—sometimes only PATH (or less). If your script fails because aws or jq isn't found, hard-code full paths or prepend with export PATH=/usr/local/bin:/usr/bin.

Robust error handling in Bash:

#!/bin/bash
set -euo pipefail

Catches unset variables, failed pipes—prevention is better than tracking silent errors later.


Relative Paths: The Underrated Source of Headaches

Scripts relying on ./config.cfg or ../data/input.csv regularly break when invoked from different working directories. Use this boilerplate to anchor paths to the script’s location:

#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cat "$SCRIPT_DIR/config.cfg"

This fails on some ancient shells; always test on each target environment for cross-team scripts.


Privilege Escalation: Always Check Effective UID

Scripts altering system state, deploying code, or modifying permissions must check who is running them. Don’t trust the environment; check explicitly.

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

Alternatively, use sudo for a single privileged operation and drop back to unprivileged execution—minimizing attack surface.


Debugging: Don’t Guess, Inspect

Typical reasons a script “doesn’t run”:

  • Permissions off (ls -l filename)
  • Shebang points to non-existent interpreter
  • Windows line endings (^M visible with cat -v)
    Fix: dos2unix myscript.sh
  • Syntax error: run with bash -n script.sh
  • Runtime tracing:
    bash -x ./myscript.sh
    
    Outputs a trace—ideal for chasing subtle logic errors.

Example real error:

bash: ./myscript.sh: /bin/bash^M: bad interpreter: No such file or directory

Classic sign of DOS line endings.


Table: Fast Reference for Robust Script Execution

StepNotes / Impact
Shebang (#!/bin/bash)Essential for predictable behavior
Explicit interpreter (e.g., bash a.sh)Avoids executability issues
Exclude . from PATHReduces exploitation risk
Full paths for commandsCron, restricted shells often lack usual $PATH
Use $SCRIPT_DIR for file refsAvoid surprises from cwd changes
Always check $EUID for root opsPrevents accidental privilege escalation
set -euo pipefailFail early and loudly
Convert line endings as neededUnix scripts fail with DOS/CR/LF endings

Not Obvious: Interpreter Version Drift

#!/usr/bin/env python3 selects the python3 in current $PATH—could be any version. Containerized environments (e.g., Alpine Linux) may not have /usr/bin/python at all.
Mitigation: for critical scripts, consider pinning the interpreter path or explicitly checking versions at runtime.

python3 --version

Side Notes

  • Systemd units set a minimal environment by default; use EnvironmentFile or Environment in service files if variables are needed.
  • Old NFS-mounted home directories may silently block chmod +x—debug with ls -l and mount output.

A script that runs “just fine” on your dev machine can quietly break on CI, in a cron job, or under a restricted container, usually due to one of the issues above. Closing these gaps means fewer midnight incidents.

Experienced engineers always assume hostile environments. That’s why their scripts rarely fail in production.