Writing Your First Script¶
Overview¶
A one-liner in history is not automation. This tutorial turns commands into a reviewable script with a clear contract: inputs, side effects, and exit status.
This is Tutorial 2 in Module 2: Writing Your First Script of the REBASH Academy Shell Scripting for DevOps Engineers series — written for Linux administrators, DevOps engineers, SREs, and platform engineers who automate production hosts with Bash.
Prerequisites¶
- Shell Fundamentals — Bash vs sh and Execution
- Bash 4.2+ on Linux (WSL2/VM/cloud)
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Apply the core ideas of “Writing Your First Script” in a real ops script
- Use
set -euo pipefailas the production default - Use quoted expansions and clear stderr diagnostics
- Produce meaningful exit codes for automation consumers
- Debug behaviour with
bash -xwhen something fails - Relate this topic to day-to-day Linux admin and DevOps work
Architecture¶
Ops scripts sit between humans/automation and system tools. This topic’s control points are shown below.
Theory¶
What it is¶
A shell script is a text file that names an interpreter on the first line (the shebang), contains commands and comments, and returns an integer exit code when it finishes. Turning a one-liner into a reviewable file gives automation a clear contract: inputs, side effects, and how callers detect success or failure. From this module onward, production scripts enable strict mode (set -euo pipefail) so failures surface early instead of silently continuing.
Why it matters¶
Ad-hoc terminal commands are not automation: they lack a review trail, a stable exit status for Continuous Integration (CI), and structure teammates can reuse. Schedulers need a predictable interpreter, a documented exit taxonomy, and behaviour that does not depend on your interactive shell. A small, well-structured script is the unit of delivery for Linux admin and DevOps glue work.
How it works¶
The shebang tells the kernel which interpreter to load when you run ./script.sh:
env resolves bash from PATH. Absolute #!/bin/bash is fine when the path is guaranteed on your images. Make the file executable with chmod +x, then invoke it with ./script.sh. Without the execute bit you can still run bash script.sh. Prefer those forms for jobs; reserve source for libraries of functions that must load into the current shell.
Every process returns an exit code from 0 to 255. By convention 0 means success and non-zero means failure. Use exit N (or the last command’s status) and document a small taxonomy for teammates — for example 2 for usage errors, 3 for missing dependencies, and 4 for runtime failures.
Production skeleton from this module onward:
-e exits on command failure, -u treats unset variables as errors, and pipefail fails a pipeline if any stage fails. Put this line near the top, after the shebang. Use # comments to explain why, not what the next line already shows.
Key concepts¶
| Form | Effect |
|---|---|
./script.sh | New process; needs execute bit and a shebang |
bash script.sh | Explicit Bash; execute bit optional |
source script.sh | Runs in the current shell — can mutate it |
Exit 0 | Success contract for CI and monitoring |
set -euo pipefail | Default strict mode for ops scripts |
Common pitfalls¶
- Omitting the shebang and relying on whoever typed
bashthis time - Forgetting
chmod +xthen blaming “Permission denied” on the wrong cause - Using
sourcefor scheduled jobs soexitorcddamages the caller - Leaving scripts without a documented exit-code contract
- Skipping strict mode and discovering silent pipeline failures only in production
Hands-on Lab¶
Create a workspace for this tutorial.
Focus: create shebang script; chmod +x; exit codes; strict-mode skeleton
Step 1 – First strict-mode script¶
cat > hello-ops.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
# hello-ops: tiny contract demo
host=$(hostname -s)
echo "hello from ${host}"
exit 0
EOF
chmod +x hello-ops.sh
./hello-ops.sh
bash -c './hello-ops.sh; echo exit=$?'
Final step – Cleanup note¶
Validation¶
- Lab commands run under
~/rebash-shell/lab02/ - You can explain each Theory heading in your own words
- Failure path exits non-zero and prints diagnostics to stderr (where applicable)
- You can relate this topic to a real DevOps or Linux admin task
Code Walkthrough¶
Production Bash for Writing Your First Script always combines:
- A clear shebang (
#!/usr/bin/env bash) - Strict mode near the top (
set -euo pipefail) from Module 2 onward - Quoted expansions and explicit tests
- Functions with
localfor reusable behaviour - Documented exit codes and stderr logging
Keep scripts short enough to review in a single merge request. When logic grows (complex JSON APIs, heavy state), hand off to Python and keep Bash as the launcher.
Security Considerations¶
- Treat all external input (args, files, env) as untrusted until validated
- Never log secrets; prefer masked CI variables and secret stores
- Prefer least privilege — do not require root for file-local tasks
- Avoid
evaland unquoted expansions in destructive commands - Validate paths stay under an allow-listed root before
rmor overwrite
Common Mistakes¶
Skipping strict mode
Cron and CI hide failures that an interactive terminal would show. Fix: start with set -euo pipefail from Module 2 onward.
Unquoted path expansions
Spaces and globs rewrite your command line. Fix: always "$path" / "$@".
Assuming interactive PATH
Aliases and fancy PATH entries disappear under schedulers. Fix: set PATH or use absolute paths.
Best Practices¶
- One purpose per script; compose with functions or small binaries
- Log to stderr; reserve stdout for data or RESULT lines
- Idempotent behaviour where scheduling may overlap
- Pair every new script with a failing-path test you actually run
- Run ShellCheck in CI before merging automation
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Works in terminal, fails in cron | PATH / cwd / env | Fingerprint env; set PATH |
unbound variable | set -u | Provide defaults or export vars |
| Pipeline “succeeds” incorrectly | Missing pipefail | set -o pipefail |
[[ unexpected operator | Running under sh/dash | Fix shebang to Bash |
Summary¶
Writing Your First Script is a core skill for Linux admins and DevOps engineers automating real hosts and pipelines. Practise the lab until the failure path is as familiar as the happy path, then continue the track.
Interview Questions¶
- How does this topic show up in production Linux administration or CI?
- What failure mode appears if you ignore quoting or strict mode here?
- How would you test this behaviour under a minimal cron-like environment?
- When would you move this logic out of Bash into Python or another tool?
- What exit code contract would you document for teammates?
Sample answer — question 2
Unquoted expansions and missing pipefail create silent or partial failures — especially under cron — that look healthy in monitoring until data is wrong.
Related Tutorials¶
- Shell Scripting for DevOps Engineers – Category Overview
- Shell Fundamentals — Bash vs sh and Execution (previous)
- Variables, Quoting, and Arithmetic (next)
- Learning Paths