Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Your first Bash script: create and run it step by step

By updated

Improve this page

Never written a script? This page is the shortest path to a first Bash script that works: a file, a shebang line, the execute permission, and it runs. In a few minutes, you will know how to create, make executable and run a script, with variables and arguments, with no programming background at all. For conditions, loops and functions, the complete Bash shell scripting guide takes over.

What you will learn

  • Create a script with the shebang #!/bin/bash
  • Make it executable with chmod +x
  • Use variables to store values
  • Read user input with read
  • Get the arguments passed to the script
  • Exit cleanly with an exit code

How do you create a minimal script?

A script is nothing more than a text file holding the commands you would have typed by hand. The 5 steps below produce a complete one, and two of them often surprise people: the file must receive the execute permission, and running it takes a ./ in front of its name.

  1. Create a file named hello.sh:

    Terminal window
    nano hello.sh
  2. Write this content:

    #!/bin/bash
    echo "Hello, welcome to $(hostname)!"
  3. Save and exit (Ctrl+O, Enter, Ctrl+X).

  4. Make the file executable:

    Terminal window
    chmod +x hello.sh
  5. Run the script:

    Terminal window
    ./hello.sh
Output
Hello, welcome to server01!

What is the shebang?

The first line of a script must be the shebang, which names the interpreter that will read the rest of the file:

#!/bin/bash

It tells the system which interpreter to use to run the file. Without this line, the kernel does not know that it is a Bash script.

How do you make a script executable and run it?

By default, a new file is not executable. Two steps are needed, chmod +x then the launch with ./:

Terminal window
chmod +x my-script.sh
./my-script.sh

The ./ tells the shell to look for the file in the current directory, instead of the directories of the PATH.

There is an alternative, useful when you are not allowed to change the file's permissions: call Bash directly on the script.

Terminal window
bash my-script.sh

The interpreter is then chosen by the command, not by the shebang, which is simply ignored. It is handy for a quick test, but the standard method remains chmod +x followed by ./my-script.sh: it makes the script usable like any other command.

How do you use variables?

A variable stores a value so you can reuse it, and $(...) fills one with the output of a command:

#!/bin/bash
NAME="Alice"
MACHINE=$(hostname)
echo "Hello $NAME, you are on $MACHINE"

Three rules apply, the same as on the command line:

  • No spaces around the =
  • To read the value: $NAME
  • $(command) captures the result of a command

Naming good practices

These conventions are not enforced by Bash, which would accept far worse: they exist so that the script stays readable months later, by you or by someone else. The most rewarding is the second one, a descriptive name, because it removes the need to go back up the code to find out what a variable holds.

ConventionExample
Upper case for global variablesDIRECTORY, FILE_NAME
Descriptive nameLOG_FILE rather than F
No special charactersOnly letters, digits, _

How do you read user input?

The read command pauses the script and waits for the user to type something:

#!/bin/bash
echo "What is your first name?"
read FIRSTNAME
echo "Hello $FIRSTNAME!"

To show the message and the input on the same line, use read -p with the prompt text:

Terminal window
read -p "Your first name: " FIRSTNAME
echo "Hello $FIRSTNAME!"

How do you pass arguments to a script?

A script can receive values when it is launched, written after its name:

Terminal window
./greet.sh Alice Bob

These values are available through special variables, numbered in the order of the arguments:

VariableContents
$1First argument (Alice)
$2Second argument (Bob)
$#Number of arguments (2)
$@All the arguments (Alice Bob)
$0Name of the script (./greet.sh)

A complete example, which prints each variable in turn so that you can see what it contains:

#!/bin/bash
echo "Script: $0"
echo "Argument 1: $1"
echo "Argument 2: $2"
echo "Number of arguments: $#"

Check that an argument is given

A robust script checks that the arguments it needs are present, and stops with a usage message otherwise:

#!/bin/bash
if [ -z "$1" ]; then
echo "Usage: $0 <name>"
exit 1
fi
echo "Hello $1!"

-z "$1" tests whether the first argument is empty. If so, the script prints a help message and stops.

How do exit codes work?

Every command (and every script) returns an exit code. Three values cover the needs of a first script:

CodeMeaning
0Success
1General error
2Misuse (wrong arguments)

To end a script with a given code, use exit followed by the number:

Terminal window
exit 0 # everything went fine
exit 1 # an error occurred

To check the exit code of the last command, print $? right after it:

Terminal window
echo $?

How do you write comments?

A comment starts with #. The shell ignores everything that follows on the line:

#!/bin/bash
# This script prints a welcome message
NAME="Alice" # Variable holding the first name
echo "Hello $NAME"

Comment your code to explain why you do something, not what (the code already shows that).

Complete example: system report

Here is a concrete script that gathers information about the system, then the two commands that run it:

#!/bin/bash
# system-report.sh: shows a summary of the system state
echo "=== System report ==="
echo "Date : $(date)"
echo "Machine : $(hostname)"
echo "User : $USER"
echo "Directory: $PWD"
echo ""
echo "Disk space:"
df -h /
echo ""
echo "Memory:"
free -h
echo "=== End of report ==="
Terminal window
chmod +x system-report.sh
./system-report.sh

This script only chains commands you already know. That is the principle of a script: automate a sequence of repetitive tasks.

Troubleshooting

The failures of a first script all look alike: the file is there, its contents are correct, and nothing runs. Three causes cover most cases, a missing execute permission, a forgotten ./ in front of the name, and a file saved with Windows line endings. None of them concerns the code itself.

SymptomLikely causeFix
Permission deniedScript not executablechmod +x my-script.sh
command not found when running script.sh./ missing in front of the nameRun it with ./script.sh
cannot execute: required file not foundWrong shebang or Windows characters (CRLF)Check #!/bin/bash on the first line, convert with dos2unix
Empty variable in the scriptWrong name or $ forgottenCheck the exact name and the $ in front
[-z: command not foundMissing space after [ in the testWrite [ -z "$1" ] with spaces
Script that never stopsImplicit loop or read waitingCtrl+C to interrupt

The bad interpreter message no longer exists

If you are looking for bad interpreter: No such file or directory, the message most tutorials still show, you will not find it. Bash 5.2 changed the wording, and it is now the same message for a wrong shebang and for a file saved with Windows line endings:

bash: ./my-script.sh: cannot execute: required file not found

The exit code is 127. The cause stays the same: the kernel cannot find the interpreter given after the #!. In the CRLF case, the interpreter looked for is literally /bin/bash\r, with an invisible carriage return at the end, hence a file "not found" although /bin/bash does exist.

Terminal window
file my-script.sh # "with CRLF line terminators" gives the culprit away
dos2unix my-script.sh

Put it into practice

You only understand a script by writing it yourself, then running it on real data. This lab has you produce a report.sh that reads a status file passed as an argument, counts the UP/DOWN hosts with a loop and a condition, then returns a non-zero exit code as soon as a host is down. Your script is compared with the expected behaviour.

Key points

  • A script starts with #!/bin/bash (the shebang), which identifies the interpreter.
  • chmod +x makes the file executable, ./script.sh runs it.
  • Variables (NAME="value") and arguments ($1, $2) make a script flexible.
  • read asks for input, echo prints a result.
  • exit 0 signals a success, exit 1 an error, which you can test with echo $?.
  • Comment your scripts to make them understandable.

Knowledge check

Check that the essentials of this guide stuck. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn