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
+------------------------------------------+| 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:
#!/usr/bin/env bashset -euo pipefailIFS=$'\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 loopsPractical Commands
## Check bash versionbash --version## GNU bash, version 5.1.16 ## Key bash features for DevOps: ## Command substitutionDATE=$(date +%Y%m%d)HOSTNAME=$(hostname -f) ## ArithmeticFILES=$((TOTAL - DELETED)) ## String operationsFILENAME="deploy-2024-01-15.tar.gz"BASENAME="${FILENAME%.tar.gz}" ## remove suffix: deploy-2024-01-15EXT="${FILENAME##*.}" ## extension: gzUPPER="${FILENAME^^}" ## uppercase ## ArraysSERVERS=("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 ## Functionslog() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"} log "Deployment started" ## Error handling with trapcleanup() { log "Cleaning up temporary files" rm -f /tmp/deploy.lock}trap cleanup EXIT ## Conditional with exit codeif systemctl is-active --quiet payment-api; then log "Service is running"else log "Service is down, starting..." systemctl start payment-apifiTroubleshooting
| 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 |
TipUse
bash -x script.shto 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.
SecurityNever use
evalwith user input or external data in bash scripts.evalexecutes arbitrary code and is the most common source of shell injection vulnerabilities. If you find yourself reaching foreval, 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.