Your First Bash Script
Your First Bash Script
Every production system you will ever work on runs shell scripts — backup jobs, deployment hooks, health checks, log rotation, CI pipeline steps. Bash is not a toy language you learn once and forget; it is the glue that holds infrastructure together at companies like Google, Meta, and Amazon. This lesson builds the foundation: how a script is structured, how the shell finds and executes it, and the habits that separate a throwaway one-liner from a production-grade automation file.
What Is a Shell Script?
A shell script is a plain text file containing a sequence of commands that the shell — most commonly bash — reads and executes line by line. There is no compiler step; the interpreter reads your file directly. That simplicity is its power and its danger: a bad command in a production script runs immediately, with no type checker to warn you.
The Shebang Line
The very first line of every script must declare which interpreter should run it. This two-character sequence #! followed by the interpreter path is called the shebang (also written hashbang).
Why /usr/bin/env bash and not /bin/bash? On most Linux servers /bin/bash works fine, but macOS, Alpine Linux, and some minimal container images place Bash at different paths. Using env delegates the lookup to the system PATH, making the shebang portable across environments — a critical property for scripts that run on developer laptops, CI runners, and production servers alike.
#!/usr/bin/env bash for all portable scripts. Reserve #!/bin/bash only when you are certain of the deployment target and need the explicit path for security reasons (e.g., hardened containers where PATH is deliberately minimal).
If the shebang is missing, the kernel falls back to /bin/sh — which on Debian/Ubuntu is dash, not Bash. Your Bash-specific syntax (arrays, [[, local, process substitution) will silently break. Always include the shebang.
Script Structure: The Anatomy of a Production Script
A well-structured script is immediately readable by anyone on your team — including the on-call engineer at 3 AM who has never seen your code before.
Notice the structure: shebang, a header block with purpose and usage, a configuration section, then logic. This layout costs you 30 seconds to write and saves the next engineer 30 minutes of guessing.
Making a Script Executable
A text file is not runnable until you grant it execute permission. The canonical command is:
The permission model matters in production. A script that runs as root and is world-writable is a privilege-escalation vulnerability. For system automation scripts (cron jobs, systemd services), use chmod 700 and ensure the file is owned by the correct service account — not root unless absolutely necessary.
How to Run a Script
There are three distinct ways to execute a script, and the difference between them is not trivial:
./deploy-check.sh— Runs the script in a subshell. The kernel reads the shebang, spawns a new Bash process, and your current shell is unaffected. This is the correct way to run most scripts.bash deploy-check.sh— Explicitly invokes Bash, overriding the shebang. Useful when the file is not yet executable or when you are debugging. Also runs in a subshell.source deploy-check.sh(or. deploy-check.sh) — Runs the script in the current shell process, no subshell. Variable assignments andcdcommands affect your current session. Used for environment-setup scripts (e.g., activating a virtualenv, loading secrets from a file).
exit, it closes your entire terminal session, not just the script. If it unsets a variable you rely on, that variable is gone from your shell. Only source a script when you explicitly want it to modify your environment.
Script Execution Flow: How the Kernel Reads Your File
Your First Real Script
Let us write a script that a DevOps engineer would actually use: a system information summary that could run as a pre-check before deployment.
Save this as sysinfo.sh, run chmod +x sysinfo.sh, then execute with ./sysinfo.sh. You will immediately see why shell scripts are indispensable: in eight lines you assembled output from six different system sources into a coherent report.
scripts/ directory committed to your repository. Name scripts with verbs (build.sh, deploy.sh, check-health.sh). Add a README documenting each script's purpose and required environment variables. This is standard practice at companies with large infrastructure codebases.
Comments and Self-Documentation
Bash comments start with # and run to the end of the line. Unlike many languages, shell scripts often live for years with little modification, touched by engineers who were not on the team when they were written. Treat comments as operational documentation:
- Explain why, not what.
# retry 3 times to handle transient DNS failuresis useful.# loop 3 timesis not. - Document external dependencies: which tools must be installed, which environment variables must be set.
- Mark any non-obvious workarounds with a note explaining the underlying issue.
Common Failure Modes
Knowing what goes wrong before it happens in production is half the job:
- Permission denied on execution — you forgot
chmod +x. Fix:chmod +x script.sh. - Bad interpreter: No such file or directory — the shebang path is wrong, or the script was edited on Windows and has CRLF line endings (
\r\n). Fix line endings withsed -i 's/\r//' script.shordos2unix script.sh. - Command not found when running via cron or systemd — non-interactive shells have a minimal
PATH. Fix: setPATHexplicitly near the top of your script (PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin). - Script works as you but fails as the service account — the service account lacks read permission on a file your script needs, or its home directory does not exist. Always test scripts under the same user that will run them in production.
With these fundamentals in place — shebang, permissions, execution modes, and structure — you have everything you need to write scripts that your teammates can read, your on-call rotation can debug, and your automation systems can run reliably. Every subsequent lesson in this tutorial builds on this foundation.