CodeRigor

LESSON

Name values with variables

Store text and whole numbers under names, read them back with a quoted $name, calculate with $(( )) and place values in a line of output.

A script that can only print fixed text is a sign. To remember something and work with it, a script gives the thing a name. A value is a piece of information, such as the text Ada or the number 3. A variable is a name that refers to a value.

Give a value a name

Bash
name="Ada"
echo "Hello, $name!"

This prints Hello, Ada!.

The first line is an assignment: it makes the name name refer to the text Ada. The rule for writing one is strict: the name, then =, then the value, with no spaces around the =.

In the second line, $name means "the value that name refers to". Before Bash runs the echo command it replaces $name with Ada. So you write the name on its own to set a variable, and with a $ in front to read it.

Bash replaces $name before the command runs
start / 3
Bash first swaps $name for the value it refers to. Only then does echo run, and it never sees the variable, only the finished text.
Bash replaces $name before the command runs
Bash first swaps $name for the value it refers to. Only then does echo run, and it never sees the variable, only the finished text.

A variable name may contain letters, digits and underscores (_), and must not start with a digit. Use lower-case names that say what the value is: price, first_name, total_cost.

A variable can be given a new value at any time. The name then refers to the new value, and the old one is forgotten.

Bash
colour="red"
echo "The light is $colour."
colour="green"
echo "The light is $colour."

This prints The light is red. and then The light is green..

The space mistake

Putting spaces around = is the most common mistake in a first Bash script, because nearly every other programming language allows it.

Bash
name = "Ada"
echo "Hello, $name!"
Text
solution.sh: line 1: name: command not found
Hello, !

Read the message as in the last lesson: line 1, the word name, "command not found". With a space after it, Bash reads name as the name of a command, with = and Ada as its arguments. No such command exists. Nothing was assigned, so the second line prints an empty space where the name should be.

That second line shows another thing to remember: a variable that was never set is empty, and using it is not an error. If a value is missing from your output, look for a misspelt variable name.

Always write "$name" inside double quotes

Whenever you read a variable, put it inside double quotation marks. Without them, Bash splits the value at its spaces and treats some characters, such as *, as special instructions, so the command can receive something different from what the variable holds. Inside double quotes the value always arrives as one unchanged piece.

Bash
title="Tea    for two"
echo $title
echo "$title"

This prints:

Text
Tea for two
Tea    for two

Without quotes the value was split into three separate words and the extra spaces were lost. With quotes it is printed exactly. The next unit explains the splitting rules in depth. For now the habit is enough: every $name goes inside double quotes.

Single quotation marks are different: inside them nothing is replaced. They mean "exactly these characters".

Bash
name="Ada"
echo "Hello, $name"
echo 'Hello, $name'

This prints Hello, Ada and then Hello, $name.

When a letter, digit or underscore follows straight after a variable, wrap the name in curly brackets so that Bash can see where the name ends:

Bash
fruit="apple"
echo "Two ${fruit}s"
echo "Two $fruits"

This prints Two apples and then Two with nothing after it: $fruits is a different variable, which was never set, so it is empty.

Whole numbers and arithmetic

A whole number is called an integer. Bash calculates with integers inside $(( )), which is replaced by the result of the calculation.

Bash
echo "$(( 7 + 2 ))"
echo "$(( 7 - 2 ))"
echo "$(( 7 * 2 ))"
echo "$(( 7 / 2 ))"
echo "$(( 7 % 2 ))"

This prints 9, 5, 14, 3 and 1. The symbols +, -, *, / and % are called operators: each one stands for an action on the values either side of it.

  • * means multiply.
  • / divides and drops the remainder: 7 divided by 2 is 3, remainder 1, so the answer is 3.
  • % gives that remainder: 1.

As in school arithmetic, *, / and % are worked out before + and -, and round brackets come first: $(( 2 + 3 * 4 )) is 14, and $(( (2 + 3) * 4 )) is 20.

Inside $(( )) you write variable names without the $. The result can be given a name like any other value:

Bash
price=3
quantity=4
total=$(( price * quantity ))
quantity=5
echo "Total: $total"

This prints Total: 12, not 15. Line 3 worked out 3 * 4 at that moment and total refers to the result, the number 12. Changing quantity afterwards does not go back and redo the calculation.

What each name refers to, line by line
start / 5 ⇄ code
An assignment works out the value on its right once, then makes the name refer to the result. Later changes to quantity do not change total.
What each name refers to, line by line
An assignment works out the value on its right once, then makes the name refer to the result. Later changes to quantity do not change total.

To change a variable using its own value, calculate and assign back to the same name:

Bash
score=10
score=$(( score + 5 ))
echo "Score: $score"

This prints Score: 15.

Bash has no decimal arithmetic

Many languages can calculate with numbers such as 2.5. Bash cannot: it has no decimal arithmetic of its own. $(( )) works with integers only.

You can still store 2.50 in a variable and print it, because to Bash it is text made of four characters. Calculating with it fails:

Bash
price=2.50
echo "$(( price * 2 ))"
Text
solution.sh: line 2: 2.50: arithmetic syntax error: invalid arithmetic operator (error token is ".50")

The message names line 2 and the value 2.50. The "error token" is the part Bash could not read as arithmetic: everything from the decimal point onwards.

The usual answer is to count in the smallest unit, so that every number is whole. Count money in pence instead of pounds, or lengths in millimetres instead of metres. To show pence as pounds, divide by 100 for the pounds and take the remainder for the pence that are left:

Bash
price=250
quantity=3
total=$(( price * quantity ))
echo "Total in pence: $total"
echo "That is $(( total / 100 )) pounds and $(( total % 100 )) pence."

This prints Total in pence: 750 and That is 7 pounds and 50 pence.

Put values into a line with printf

Inside double quotes, echo joins text and values well. printf gives more control. Its first argument is a format: the line to print, with a placeholder wherever a value belongs. The values follow as further arguments, in order.

Bash
item="Notebook"
quantity=3
printf '%s: %d in stock\n' "$item" "$quantity"
printf 'Price: %d.%02d\n' 7 5

This prints Notebook: 3 in stock and Price: 7.05.

  • %s is a placeholder for text.
  • %d is a placeholder for an integer.
  • %02d is an integer shown with at least two digits, with a zero in front when needed. That is how 5 pence becomes 05.

The format is written in single quotes because it is meant exactly as typed; the values, which come from variables, are in double quotes. Remember that printf ends the line only where you write \n.

What you can do now

You can name text and whole numbers, read them back with a quoted "$name", calculate with $(( )), and build a line of output from text and values. You also know two silent traps: spaces around =, and a misspelt variable that is empty. In the exercises you will predict what small scripts print, then write a script that calculates a receipt.

Next: read what the user types, so that the values can come from the person running the script.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →