Chapter 02Lesson 01~40 minutes

From Interactive Commands to Executable Bash Scripts

Interactive commands are excellent for exploration. Scripts turn those commands into repeatable automation. This lesson shows how to make that transition without smuggling hidden terminal state into production workflows.

BeginnerBash scriptingHands-on lab

Learning objectives

By the end of this lesson

  • Explain what changes when commands move from an interactive shell into a script file.
  • Create and execute a small Bash script using a predictable project layout.
  • Use comments, blank lines, variables, and exit status to make script intent explicit.
  • Identify hidden dependencies such as the current directory, environment variables, aliases, and interactive prompts.
  • Build a first diagnostic script that behaves consistently when run more than once.

1. A script creates an automation boundary

A Bash script is a text file containing shell language that Bash reads and executes. The important change is not simply “commands stored in a file.” A script creates a repeatability boundary: another person, a CI runner, a cron job, or a container can execute the same instructions later.

That boundary forces you to make assumptions visible. Interactive work often depends on things you do not notice: your current directory, aliases, shell options, exported variables, credentials already loaded into an agent, or files created by an earlier command. Reliable scripts reduce those invisible dependencies.

ConceptMeaningOperational note
Interactive commandExecuted in the shell you are currently usingCan depend on aliases, shell history, current state, and human judgment
Script fileRead as a program by a shellShould declare assumptions, inputs, outputs, and failure behavior
Automation jobRuns a script non-interactivelyUsually has a smaller environment and no person available to answer prompts
DevOps principle

A good script should be understandable from the file and its documented inputs—not from the author's terminal history.

2. Build the smallest useful script

Create a dedicated course directory and write a script that reports its execution context. The first line will be discussed in depth in the next lesson; for now, treat it as a declaration that this file is intended for Bash.

mkdir -p "$HOME/devops-academy/bash/chapter02/lesson01"
cd "$HOME/devops-academy/bash/chapter02/lesson01"

cat > context-report.sh <<'EOF'
#!/usr/bin/env bash

printf 'user=%s\n' "$(whoami)"
printf 'shell_pid=%s\n' "$$"
printf 'working_directory=%s\n' "$PWD"
printf 'bash_version=%s\n' "$BASH_VERSION"
EOF

bash context-report.sh

Notice that the script is initially launched with bash context-report.sh. This does not require the file itself to be executable because you are explicitly asking the bash program to read it.

3. Understand the execution context

Execution model
flowchart TD
  U["Your terminal"] --> P["bash context-report.sh"]
  P --> C["Child Bash process"]
  C --> S["Read script lines"]
  S --> X["Run commands and expansions"]
  X --> E["Return final exit status"]

When you run bash script.sh, Bash normally starts as a separate process. It inherits exported environment variables and the current working directory, but it does not automatically inherit every interactive convenience.

  • Aliases are normally not expanded in non-interactive shells.
  • Shell-local variables that were never exported are not inherited by child processes.
  • Interactive startup files and non-interactive startup behavior differ.
  • A script may be launched from any directory, so relative paths need deliberate design.

4. Remove hidden dependencies before automation

A quick command often “works on my machine” because the terminal has accumulated state. Before scripting a workflow, inventory that state.

# Inspect likely dependencies before scripting.
printf 'PWD=%q\n' "$PWD"
printf 'PATH=%q\n' "$PATH"
type -a git
type -a curl
alias 2>/dev/null || true
env | sort | less

For production automation, prefer explicit commands and well-defined configuration. Do not make a script depend on an alias such as alias kubectl='kubectl --context prod'. A CI runner will not share that alias, and silently using a different context could be dangerous.

Operational warning

If a command can alter infrastructure, deploy software, or delete data, hidden context is a correctness and safety risk.

5. Give scripts a readable structure

Even a small script benefits from a consistent shape: interpreter declaration, short purpose comment, configuration, main work, and explicit reporting. You do not need enterprise ceremony for ten lines of Bash, but you do need enough structure to make future edits safe.

#!/usr/bin/env bash
# Print a concise workspace report.

workspace=${1:-"$PWD"}

printf 'Workspace: %s\n' "$workspace"
printf 'Files: '
find "$workspace" -maxdepth 1 -type f -printf '.' 2>/dev/null | wc -c

printf 'Git repository: '
if git -C "$workspace" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  printf 'yes\n'
else
  printf 'no\n'
fi

The positional argument $1 and the ${1:-...} expansion will be covered carefully later. For now, observe the design: the script has one optional input and a documented fallback.

6. Make success and failure observable

Every command returns an integer exit status. By convention, zero means success and non-zero means some kind of failure. A script also returns an exit status to its caller. CI systems, service managers, and orchestration tools use that signal to decide whether a step succeeded.

cat > check-directory.sh <<'EOF'
#!/usr/bin/env bash

target=${1:-}

if [[ -z $target ]]; then
  printf 'usage: %s DIRECTORY\n' "$0" >&2
  exit 64
fi

if [[ ! -d $target ]]; then
  printf 'error: not a directory: %s\n' "$target" >&2
  exit 1
fi

printf 'directory exists: %s\n' "$target"
exit 0
EOF

bash check-directory.sh /tmp
printf 'status=%s\n' "$?"

bash check-directory.sh /definitely/not/here
printf 'status=%s\n' "$?

Explicit exits are not required everywhere, but they are useful when a script has a clear contract. Later chapters will cover robust error propagation, set -e, pipefail, traps, and structured error messages.

7. Hands-on lab: convert a command sequence into a script

Create a script that captures a small repository-independent machine snapshot. It should have no destructive behavior and should create its output directory if necessary.

mkdir -p "$HOME/devops-academy/bash/chapter02/lesson01/lab"
cd "$HOME/devops-academy/bash/chapter02/lesson01/lab"

cat > snapshot.sh <<'EOF'
#!/usr/bin/env bash

output_dir=${1:-"./output"}
mkdir -p "$output_dir"

report="$output_dir/system.txt"

{
  printf 'timestamp=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  printf 'user=%s\n' "$(whoami)"
  printf 'host=%s\n' "$(hostname)"
  printf 'pwd=%s\n' "$PWD"
  printf 'bash=%s\n' "$BASH_VERSION"
} > "$report"

printf 'wrote %s\n' "$report"
EOF

bash snapshot.sh
cat output/system.txt
bash snapshot.sh second-run
cat second-run/system.txt

Verification checklist

8. Knowledge check

Question 1. Why is a script more than a saved terminal history?

Question 2. Does bash script.sh require executable permission on script.sh?

Question 3. Why does exit status matter in DevOps automation?

9. Summary

Moving commands into a Bash file changes the engineering problem. You are no longer only making commands work once; you are designing a repeatable program with inputs, assumptions, outputs, and an observable result. The next lesson focuses on exactly how an executable script is selected and launched.

10. Further reading

  • GNU Bash Reference Manual — shell scripts, invocation, shell operation, and exit status.
  • POSIX Shell Command Language — portable shell execution concepts.
  • Linux man-pages: execve(2) — how a process asks the kernel to execute a program.
  • ShellCheck documentation — common script correctness and portability diagnostics.
Next lesson

Shebangs, Execution Permissions, and Script Launch Methods

Continue Chapter 2 by building on this execution and data model.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.