Variables in Shell Scripting

  • Variables are placeholders to store data (strings, numbers, paths, etc).
  • They don’t require explicit declaration type (untyped).
  • Stored as strings by default in bash/sh.

Creating Variables

name="Ahmad"
age=20

Note: ⚠️ No spaces around =.

Using Variables

echo $name
echo "I am $age years old"
  • $var → expands value(referencing the value of a variable)
  • ${var} → safer form, avoids ambiguity, and useful for appending
file="report"
echo "${file}_2025.txt"   # report_2025.txt

Types of Variables

User-defined variables (you create)

city="Multan"

Environment variables (system-wide, inherited by child processes)

echo $PATH
export MYVAR="hello"

Best Practices

  • Use uppercase for environment vars (PATH, HOME)
  • Use lowercase for local script vars (count, name)
  • Quote variables to avoid word-splitting
    • echo "$path"

Read-only Variables

readonly pi=3.1415
pi=4   # error

Also you can use the declare -r variable_name to make constant.

declare -r age=30

Delete variable and List Variable

List All Variables

set

It will display all variables

Delete a Variable

unset variable_name

Quoting

Single Quotes (' ')

  • Everything inside single quotes is taken literally.
  • Variables, command substitution, and escape sequences do not work.
  • You cannot put a literal single quote ' inside single quotes (unless you end and restart quoting or escape cleverly).

Example:

echo 'Hello $USER'

Output:

Hello $USER

(Here $USER is not expanded.)

Double Quotes (" ")

  • Double quotes are partially literal.
  • Special characters like $, `, and \ are still interpreted.
  • Variables and command substitution are expanded.
  • Good choice when you want text with spaces but still allow variable substitution.

Example:

echo "Hello $USER"

Output (suppose user is ahmad):

Hello ahmad

Another example with command substitution:

echo "Today is $(date)"

Output:

Today is Mon Sep 09 22:20:15 PKT 2025

Escape Character (\)

  • Backslash escapes the next character, meaning it loses its special meaning.
  • Useful to print characters like $, ", ', \, or space.

Example:

echo "Hello \"World\""
# Output:
Hello "World"


echo \$HOME
# Output:
$HOME # (Instead of expanding it, it prints literally.)

Rule of Thumb

  • Use single quotes when you want exact literal text.
  • Use double quotes when you want variables and commands to expand.
  • Use backslash for one-off escapes.

Environment and Shell Local Variables

Local (Shell) Variables

  • Exist only in the current shell session (not inherited by child processes).
  • They disappear when the shell is closed.
  • Default when you create variables with VAR=value without exporting.

Example:

MYVAR="hello"
echo $MYVAR
# hello

bash   # open a subshell
echo $MYVAR
# (nothing printed, variable not passed)
exit

✔ Local to the shell, not shared with subshells or programs.

  • User configuration file: ~/.bashrc ~/.zshrc
  • System-wide configuration file: /etc/bash.bashrc and /etc/profile

Environment Variables

  • Variables that are exported to child processes.
  • Used by system programs, shells, and user applications.
  • Example: PATH, HOME, USER, LANG.

Create/Export an Environment Variable

export MYVAR="hello"
bash   # new subshell
echo $MYVAR
# hello

Save them permanently add them to your .bashrc or .zshrc etc file.

Commands for Working with Variables

set

  • Lists all variables (local + environment + shell functions).
set

printenv or env

  • Shows only environment variables.
printenv
env

export

  • Marks a variable as environment.
MYVAR="ahmad"
export MYVAR

Special Variables (Shell Built-ins)

  • $0 → Script name
  • $1, $2 → Positional parameters (arguments to script)
  • $# → Number of arguments
  • $$ → PID of current shell
  • $? → Exit status of last command
  • $USER, $HOME, $PATH → Common environment variables

Key Differences

FeatureLocal VariableEnvironment Variable
ScopeOnly in current shellIn current shell + subshells
Created withVAR=valueexport VAR=value or export VAR
LifetimeEnds when shell exitsEnds when shell exits (but survives in child shells)
ExamplesMYVAR, counterPATH, HOME, USER, LANG

Getting User Input in Shell

The read Command

  • Syntax:
read [options] variable_name
  • Reads one line of input from the user and stores it into a variable.
  • If no variable is provided, input goes into the built-in variable REPLY.

Basic Example:

#!/bin/bash
echo "Enter your name:"
read NAME
echo "Hello $NAME"

# Output
Enter your name:
Ahmad
Hello Ahmad

Using REPLY (Default Variable)

If no variable is given:

echo "Enter something:"
read
echo "You typed: $REPLY"

Prompting Without echo (Using -p)

Instead of writing a separate echo, you can directly prompt:

read -p "Enter your age: " AGE
echo "You are $AGE years old"

Silent Input (For Passwords) with -s

Doesn’t display what the user types.

read -sp "Enter password: " PASS
echo
echo "Password saved (hidden)"

Multiple Inputs in One Line

You can read multiple values at once:

read -p "Enter first and last name: " FIRST LAST
echo "First: $FIRST, Last: $LAST"

# Input
Ahmad Hassan

# Output
First: Ahmad, Last: Hassan

Useful Options Summary

OptionMeaning
-pPrompt inline
-sSilent (hide input)
-t NTimeout after N seconds
-n NRead N characters only
-aStore words into array

Rule of Thumb

  • Use read -p for interactive scripts.
  • Use read -s for sensitive info.
  • Combine options for flexibility.

Positional Parameters

What are Positional Parameters?

  • Arguments passed to a script or program from the command line are called positional parameters.
  • They are referenced by numbers ($1, $2, $3, …).
  • Each argument is separated by spaces (default IFS = Internal Field Separator).

Example:

./myscript.sh file.txt dir1 192.168.1.1
  • $0 → script name (./myscript.sh)
  • $1 → first argument (file.txt)
  • $2 → second argument (dir1)
  • $3 → third argument (192.168.1.1)

Accessing Positional Parameters

  • $n gives the nth argument.
  • {} is required for arguments after $9.
    • $9 → ninth argument
    • ${10} → tenth argument
    • ${11} → eleventh argument

Default Values for Parameters

If a parameter is missing, you can provide a default value with syntax:

${n:-default_value}

Example:

echo "First argument: ${1:-ABC}"
  • If $1 is given → it prints the actual argument.
  • If $1 is missing → it prints "ABC".

4. $0 Special Parameter

  • $0 → the name of the script as it was invoked.
  • Useful for script self-identification.

Example Script: Displaying Positional Parameters


#!/bin/bash

echo "Script name: $0"
echo "First argument: ${1:-NoArg1}"
echo "Second argument: ${2:-NoArg2}"
echo "Third argument: ${3:-NoArg3}"

Run:

./script.sh A B

Output:

Script name: ./script.sh
First argument: A
Second argument: B
Third argument: NoArg3

Practical Example: Display and Compress Script

#!/bin/bash
# display_and_compress.sh

echo "Displaying the contents of $1"
sleep 2
cat "$1"

echo
echo "Compressing $1..."
sleep 2
tar -cjvf "$1.tar.gz" "$1"

echo "Done! Archive created: $1.tar.gz"

What Are Special Parameters?

  • Special parameters are variables provided by the shell that hold important information.
  • You cannot assign values to them, only read them.

Common Special Parameters

$0

  • Expands to the name of the shell script being executed.
  • Example:
echo "Script name is $0"

Output:

Script name is ./myscript.sh

$#

  • Expands to the number of positional parameters (arguments) passed to the script.
  • Example:
./myscript.sh a b c
echo "Arguments count: $#"

Output:

Arguments count: 3

$@ and $*

  • Both expand to all positional parameters.
  • Subtle difference when quoted:
ExpressionBehavior
$@Expands each argument as a separate word. Useful in loops.
$*Expands all arguments as a single word (joined with IFS, default is space).

Example:

./myscript.sh "a b" c

for arg in "$@"; do echo "[$arg]"; done
# [a b]
# [c]

for arg in "$*"; do echo "[$arg]"; done
# [a b c]

$?

  • Expands to the exit status of the most recent command.
  • 0 = success, non-zero = error.

Example:

ls /etc
echo $?
# 0

ls /notexist
echo $?
# non-zero (error)

$$

  • Expands to the process ID (PID) of the current shell.
  • In a subshell, it still expands to the PID of the invoking shell (not the subshell).

Example:

echo "Shell PID is $$"

Exit Status Convention ($?)

  • Every command returns a status code:
    • 0 → success
    • Non-zero → error

Examples:

ls
echo $?   # 0

cat /etc/shadow
echo $?   # Non-zero (permission denied)

unknowncommand
echo $?   # Non-zero (command not found)

Quick Summary Table

ParameterMeaning
$0Name of the script
$#Number of arguments
$@All arguments (each separately, preserves quoting)
$*All arguments (as a single word)
$?Exit status of last command
$$PID of current shell

Rule of Thumb

  • Use $0 for script name, $# to check argument count, $@ for iterating arguments, $? to check success/failure, and $$ for PID when scripting background tasks or logs.

# $@ vs $* in Shell Scripting

Both $@ and $* expand to all positional parameters ($1, $2, $3, …).
The key difference appears when they are quoted.

$@ (All Positional Parameters)

  • Expands to all arguments starting from $1.
  • Unquoted $@ → behaves like $1 $2 $3 ….
  • Quoted "$@" → preserves each argument as a separate quoted string.
    • Equivalent to "$1" "$2" "$3" ….
    • Prevents word splitting inside arguments.

Example:

#!/bin/bash
for arg in "$@"; do
    echo "[$arg]"
done

Run:

./script.sh "my file.txt" report.txt doc.pdf

Output:

[my file.txt]
[report.txt]
[doc.pdf]

Note: Each argument remains intact, even with spaces.

$* (All Positional Parameters)

  • Expands to all arguments as a single string.
  • Unquoted $* → same as unquoted $@$1 $2 $3 … (word splitting occurs).
  • Quoted "$*" → treats all parameters as one single string, joined together.
    • The separator used is the first character of IFS (Internal Field Separator).
    • By default, IFS = space, so arguments are joined with spaces.

Example:

#!/bin/bash
echo "Using \"\$*\":"
for arg in "$*"; do
    echo "[$arg]"
done

Run:

./script.sh "my file.txt" report.txt doc.pdf

Output:

[my file.txt report.txt doc.pdf]

All arguments combined into one string.

Word Splitting Issue Example

#!/bin/bash
touch $@

Run:

./script.sh "my file.txt" "your report.txt" "his document.txt"
  • Without quotes, $@ = my file.txt your report.txt his document.txt
  • This causes 6 files to be created instead of 3 (my, file.txt, yourreport, txt, …).

Fix with quoting:

touch "$@"

Now only 3 files are created correctly.