Written and maintained by CASRAI Editorial Board
Published · Last updated
On this page
A shell script is a text file containing shell commands. When Bash is given such a file, it reads the commands, runs them in order and exits. That makes a script the simplest way to record a command-line procedure so it can be repeated exactly. This guide covers what happens when a script runs, why the first line (the shebang) is less portable than it looks, and how exit status tells you whether a run worked.
What a shell script is used for in research
Researchers use shell scripts to capture a sequence of command-line steps once and rerun it without retyping: moving and renaming files, calling analysis programs one after another, or wrapping a program so that it can be handed to a job scheduler. The script itself is the record of what was run, which supports the reproducibility practices described in reproducibility infrastructure for workflows, containers and code sharing. For scheduler use on a cluster, a batch script is a shell script with extra directives; see how to write an sbatch job script for the Slurm-specific parts.
What happens when a script runs
The GNU Bash Reference Manual defines a shell script as a text file containing shell commands. When Bash receives such a file as its first non-option argument (and neither -c nor -s is given), it reads and executes the commands in the file and then exits. This is a non-interactive shell: no prompt is shown and commands are not typed one by one.
- Where Bash looks for the file. If the filename contains no slash, Bash searches the current directory first and then the directories in
$PATH. - Arguments. Inside the script,
$0holds the script name and the positional parameters ($1,$2, and so on) hold the remaining arguments. - Two ways to run it. You can name the interpreter explicitly (
bash myscript.sh), or turn on the file’s execute bit withchmodand run it directly. The manual states that once the file is executable, runningfilename argumentsis equivalent tobash filename arguments.
Access: where the shell comes from
A shell is a command language interpreter (POSIX wording). Bash is the shell the GNU Project publishes; its manual and source are at gnu.org/software/bash. On a shared research computing system you normally use whichever shell the system administrators provide, so check the interpreter available to you before writing a script that depends on Bash-only features.
Step by step: write, make executable, run
- Create a plain text file, for example
run-analysis.sh, whose first line is#!/usr/bin/env bash. - Add one command per line below it. Lines starting with
#after the first are comments. - Turn on the execute bit:
chmod +x run-analysis.sh. - Run it from its directory with
./run-analysis.sh input.csv. The./matters: a bare command name is searched for only in the directories listed in$PATH, and the current directory is normally not among them, so./is needed to tell the shell to run the file in the current directory. - Check the exit status immediately afterwards with
echo $?(see below).
The shebang line and why it is not fully portable
If the first line of the file begins with #!, the rest of that line names an interpreter for the program, and optional arguments may follow. Two cautions come straight from the primary sources:
- The Bash manual notes that some older Unix systems allow only a single argument after the interpreter, so a shebang with more than one argument is not portable. It describes using
envto find Bash, as in#!/usr/bin/env bash, as a common idiom that works even if Bash is installed in another directory. - The POSIX Shell Command Language states that if the first line of a file of shell commands starts with
#!, the results are unspecified. In other words, shebang handling is up to the operating system, not guaranteed by the POSIX standard.
Output and exit status
A script writes output to standard output and standard error like any command. Whether it succeeded is carried by its exit status. POSIX defines an exit status for every command; by convention zero means success and non-zero means failure, and a command that cannot be found returns 127.
Bash gives you options to make failures louder. The Bash manual describes these options of the set builtin:
| Option | What the manual says it does |
|---|---|
set -e |
Exit immediately if a pipeline (which may be a single simple command) returns a non-zero status. The manual lists exceptions, such as commands in an if test or a while/until condition, and most commands in an && or || list. |
set -u |
Treat unset variables and parameters (other than the special parameters @ and *) as an error when performing parameter expansion. |
set -x |
Print a trace of simple commands to standard error after they are expanded and before they are executed. |
set -o pipefail |
The return value of a pipeline becomes the value of the last (rightmost) command to exit with a non-zero status, or zero if every command succeeds. |
Without pipefail, a pipeline’s status is that of its last command, so an early failure in a pipe can pass unnoticed. Placing set -euo pipefail near the top of an analysis script is a common way to stop on the first failure; set -x is the quick way to see which command a failing script was running.
Troubleshooting
Permission denied
The execute bit is not set. Run chmod +x script.sh, or run it as bash script.sh, which does not need the execute bit.
Command not found (exit status 127)
POSIX specifies status 127 when a command is not found. Common causes are a mistyped name, a program that is not on $PATH, or running the script by bare name instead of ./script.sh.
The script behaves differently on another machine
Since shebang results are unspecified by POSIX and older systems accept only one interpreter argument, keep the first line to the interpreter alone, commonly #!/usr/bin/env bash. See working on an interactive session on a compute node if the difference appears only on a cluster node.
An unset variable silently expands to nothing
Add set -u so the expansion fails instead of producing an empty string.
Bash scripts and POSIX sh scripts compared
| Question | Bash script | POSIX shell script |
|---|---|---|
| Defined by | GNU Bash Reference Manual | POSIX Shell Command Language (Open Group Base Specifications) |
| Recommended first line | #!/usr/bin/env bash (a common idiom described in the Bash manual) |
Shebang results are unspecified by the standard |
| Command not found | Exit status 127 | Exit status 127 (section 2.8.2) |
For work that outgrows a script, the scheduler-oriented guides Slurm job arrays and srun vs sbatch vs salloc show how scripts are submitted at scale.
FAQ
What is a shell script?
A text file containing shell commands that a shell reads and runs, in order, as a non-interactive shell.
Does a shell script need the .sh extension?
Neither the Bash manual nor POSIX requires a particular extension. What makes a file runnable directly is the execute bit and, optionally, the shebang line.
What is the difference between ./script.sh and bash script.sh?
For an executable file the Bash manual treats them as equivalent. Naming bash explicitly also works without the execute bit.
Is the shebang line part of the POSIX standard?
No. POSIX says the results are unspecified when the first line of a file of shell commands starts with #!.
How do I know if a script failed?
Check its exit status. Zero conventionally means success; 127 means a command was not found. Use set -e and set -o pipefail to stop on failures.
Sources
Date checked: 2026-10-29.
- GNU Bash Reference Manual, Shell Scripts (checked 2026-10-29).
- GNU Bash Reference Manual, The Set Builtin (checked 2026-10-29).
- The Open Group, POSIX Shell Command Language (checked 2026-10-29).







