Explains using guard clauses in shell scripts to fail fast, reduce nesting, and improve readability and maintainability of scripts
In this lesson we cover the guard-clause pattern for Shell scripting — a simple technique that reduces nesting by checking and failing fast on preconditions. Guard clauses make the primary, successful path of your script clear and unindented, improving readability and maintainability.
#!/bin/bash# Check if a file existsif [[ -e myfile.txt ]]; then echo "File exists"else echo "File does not exist"fi
Now compare that to a more deeply nested script that validates a user and file conditions before running a process:
Copy
#!/bin/bashif [[ "${USER_NAME}" == "admin" ]]; then if [[ -e "${FILE_PATH}" ]]; then if [[ -s "${FILE_PATH}" ]]; then run_process else echo "File exists but is empty" fi else echo "File does not exist" fielse echo "User is not admin"fiexit 0
Deep nesting like this makes control flow harder to follow and increases the cognitive load when scanning code or debugging.
A cleaner approach is to check preconditions early and exit immediately when they fail. This keeps the successful execution path flat and easy to read.Here is an idiomatic version of the previous script using guard clauses. Each critical condition is checked up front and exits on failure, leaving the main logic simple:
Copy
#!/bin/bashreadonly FILE_PATH="/home/ubuntu/guard_clause/file.txt"readonly USER_NAME="admin"run_process() { echo "running process..."}if [[ "${USER_NAME}" != "admin" ]]; then echo "User is not admin" exit 1fiif [[ ! -e "${FILE_PATH}" ]]; then echo "File does not exist" exit 1fiif [[ ! -s "${FILE_PATH}" ]]; then echo "File exists but is empty" exit 1firun_processexit 0
By reversing conditionals (using ! where appropriate) and exiting immediately on failure, the main successful path of the script is simple and unindented.
Guard clauses are like preparing tools before disassembling a car — you check essentials first so the rest of the work assumes those conditions are met.