Skip to main content

Bash

Bash (Bourne Again Shell) is the default command-line shell on Linux servers. It is both an interactive shell for daily commands and a scripting language for automation. Bash scripts power CI/CD pipelines, deployment scripts, health checks, and server automation across the industry.

Understanding Bash

What Is Bash in Simple Terms

Bash is the shell most Linux engineers spend their careers in. It is the prompt you see when you SSH into a server, the interpreter that runs your deployment scripts, and the language you use in CI/CD pipeline steps. Learning bash well is one of the highest-return investments in DevOps engineering.

How It Works

◈ DIAGRAM
+------------------------------------------+
| Bash script: deploy.sh |
| |
| #!/usr/bin/env bash |
| set -euo pipefail |
| IMAGE=$(build_image) |
| deploy $IMAGE |
+------------------------------------------+
|
bash parses and executes
|
v
+------------------------------------------+
| Each command: fork + exec |
| Variables: expanded before execution |
| Errors: caught by set -e, script exits |
+------------------------------------------+

Bash strict mode -- the three lines every script needs:

Bash
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
## set -e Exit immediately on any command failure
## set -u Treat unset variables as errors
## set -o pipefail Pipeline fails if any command fails
## IFS Safer word splitting in for loops

Practical Commands

Bash
## Check bash version
bash --version
## GNU bash, version 5.1.16
## Key bash features for DevOps:
## Command substitution
DATE=$(date +%Y%m%d)
HOSTNAME=$(hostname -f)
## Arithmetic
FILES=$((TOTAL - DELETED))
## String operations
FILENAME="deploy-2024-01-15.tar.gz"
BASENAME="${FILENAME%.tar.gz}" ## remove suffix: deploy-2024-01-15
EXT="${FILENAME##*.}" ## extension: gz
UPPER="${FILENAME^^}" ## uppercase
## Arrays
SERVERS=("10.0.1.50" "10.0.1.51" "10.0.1.52")
for server in "${SERVERS[@]}"; do
ssh "rahul@$server" 'sudo systemctl restart nginx'
done
## Functions
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
log "Deployment started"
## Error handling with trap
cleanup() {
log "Cleaning up temporary files"
rm -f /tmp/deploy.lock
}
trap cleanup EXIT
## Conditional with exit code
if systemctl is-active --quiet payment-api; then
log "Service is running"
else
log "Service is down, starting..."
systemctl start payment-api
fi

Troubleshooting

Symptom Command What to Check
Script continues after error Add set -e Error handling not enabled
Unbound variable crash Add set -u Variable used before being set
Syntax error bash -n script.sh Check syntax without running
Logic error bash -x script.sh Trace execution line by line
Tip

Use bash -x script.sh to trace script execution. Every line is printed with a + prefix before it runs. This is the fastest way to find where a script is going wrong and what values variables actually contain at runtime.

Security

Never use eval with user input or external data in bash scripts. eval executes arbitrary code and is the most common source of shell injection vulnerabilities. If you find yourself reaching for eval, there is almost always a safer approach using arrays, parameter expansion, or explicit parsing.

Frequently Asked Questions

Why is Bash still the default choice for CI/CD and deployment scripting despite newer alternatives?

Bash is preinstalled on virtually every Linux server and CI runner, so a Bash script has zero additional runtime dependency to install — unlike Python or a compiled tool, which need their own interpreter or binary present. It also composes naturally with the Unix toolchain (pipes, `grep`, `awk`, `curl`) that most infrastructure automation is already built around, which is why entrypoint scripts, pre-push hooks, and CI pipeline steps default to `#!/bin/bash` rather than a general-purpose language.

What's a common Bash scripting mistake that causes production incidents?

Omitting `set -euo pipefail` at the top of the script. Without it, a failed command in the middle of a deployment script doesn't stop execution — Bash just continues to the next line, so a failed `docker push` or a missing environment variable silently gets treated as success and the script proceeds to run subsequent steps against a half-finished state. `set -e` exits on any non-zero return, `-u` catches unset variable references, and `-o pipefail` makes a failure anywhere in a pipe chain propagate instead of being swallowed.