A shell script is a file of commands the shell runs in order — and the first one teaches three things at once: how the shell knows what will execute it, how the system knows it may, and where scripts live so you can type their name instead of their path.
The script and its shebang #
#!/usr/bin/env bash
echo "Hello from $(whoami) on $(hostname)"
The first line is the shebang. It is not a comment to the shell that runs the file — it is advice to the system about which interpreter to use when the file is executed directly. #!/usr/bin/env bash finds bash through PATH, which keeps the script working on machines where bash lives elsewhere than /bin.
Making it executable #
Writing the file is not enough; the execute bit is:
$ chmod +x hello.sh
$ ./hello.sh
Hello from ana on laptop
$ bash hello.sh # also works, no execute bit needed
The two invocation styles differ in one important way: ./hello.sh uses the shebang and runs as its own program; bash hello.sh just feeds the file to bash. Scripts intended for ./ deserve the shebang; see chmod for the permission bits involved.
Where scripts live #
Typing ./ forever gets old. Any directory in PATH will do:
$ mkdir -p ~/.local/bin
$ echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
$ cp hello.sh ~/.local/bin/hello && hello
Many distros already include ~/.local/bin in PATH when it exists. The full mechanics are in PATH and environments.
The habits that scale #
From script one, build the reflexes that matter at script one hundred:
- Start with
#!/usr/bin/env bash— or#!/bin/shif you intend portability. - Quote every variable:
"$file", always. The quoting rules explain what unquoted expansion does to spaces. - Add
set -euo pipefailonce you care about failures; strict mode has its own note. - Comment the why, not the what.
Next steps in order: variables, conditionals, and loops — the whole scripting series builds a working toolkit.
