Mastering .sh Script Execution from the Terminal
Direct script execution on Unix-like systems—Linux (tested on Ubuntu 22.04), macOS (12+), BSD—remains the fastest and most auditable way to automate system administration, development workflows, or CI/CD steps. GUIs sometimes obscure failure states, and third-party “run script” tools can introduce subtle path or environment issues.
Pre-Execution: Validate and Secure
1. Always Inspect Third-Party Scripts
Misplaced trust kills systems. Open scripts in a safe editor:
less myscript.sh
# or for syntax highlighting, if available:
vim myscript.sh
Scan for unexpected rm
, curl|sh
, or privileged operations (sudo
, chown
, etc). Ignore these at your peril.
2. Confirm Shebang Correctness
The shebang (#!/bin/bash
) pins interpreter behavior. Bash and sh diverge notably post v4.0—assume nothing about script portability.
Interpreter Line | Typical Path | When to Use |
---|---|---|
#!/bin/bash | /bin/bash | Consistent bash features |
#!/usr/bin/env bash | env-resolved | Cross-platform scripts |
Side note: Some enterprise images (Alpine, minimal containers) lack /bin/bash
; prefer POSIX sh for maximal portability when possible.
3. Grant Execute Permission
Skip this and you’ll hit:
bash: ./myscript.sh: Permission denied
Fix:
chmod +x myscript.sh
Options for Execution
(A) Direct Execution with ./
Current directory is (deliberately) omitted from PATH
on modern Linux/macOS. Avoids accidental script hijacking by similarly named binaries. Thus:
./myscript.sh
This invokes the interpreter specified in the script's shebang. Useful if the script leverages Bashisms ([[ ]]
, arrays).
Error Handling Example
If the shebang points to a non-existent interpreter:
./myscript.sh
bash: ./myscript.sh: /usr/bin/fakeshell: bad interpreter: No such file or directory
(B) Interpreter-Explicit
Hand control to the desired shell. This skips shebang logic altogether. Useful for debugging, or mismatched interpreter requirements:
bash myscript.sh # Forces bash, ignores '#!line'
sh myscript.sh # Use POSIX shell; beware Bash extensions
Side Effect: Executable bit (chmod +x
) is not required here.
Practical Example
Consider deploy.sh
:
#!/bin/bash
set -e
echo "Deploying app to $1"
rsync -avz ./ "$1"
echo "Done."
Minimal production usage:
chmod +x deploy.sh
./deploy.sh /mnt/staging
If permissions lag:
bash deploy.sh /mnt/staging
Trade-off: shebang-reliant scripts may break with different interpreters; e.g., [[
syntax error with dash.
Safety and Robustness
-
Never
sudo
a script you haven't inspected. -
Set
set -euo pipefail
early for stronger error handling. Gotcha: Not all shells supportpipefail
. -
Use full paths (
/usr/bin/rsync
) in production or cron, to avoid confusion with userPATH
. -
Scripts that mutate system state: log actions, e.g.,
logger "Starting backup of /srv at $(date)"
-
Validate input arguments:
if [[ -z "$1" ]]; then echo "Usage: $0 <destination>" exit 1 fi
Non-Obvious Tip: Argument Forwarding
Lesser-known: forward all arguments using "$@"
for script wrappers.
#!/bin/bash
set -e
echo "Invoking downstream tool with arguments: $*"
./internal-tool "$@"
Benefit: Preserves whitespace and quoting for downstream commands (unlike $*
).
Table: Script Execution Flow
Step | Command | Potential Pitfall |
---|---|---|
Read script | less script.sh | Malicious/erroneous code |
Set permissions | chmod +x script.sh | Forgotten = Permission denied |
Execute in-place | ./script.sh | Wrong interpreter; missing deps |
Or: via interpreter | bash script.sh | Ignores shebang, masking issues |
Final Thoughts
Executing .sh
scripts directly maximizes transparency and control but assumes you’re vigilant with permissions, interpreter selection, and script content. There’s always a trade-off: explicitness vs. speed, portability vs. feature set. Don’t trust, do verify.
Known issue: On macOS, default shells may point to Zsh as /bin/sh
; behavior may subtly differ from Ubuntu running Dash or GNU Bash.