Shell Scripting Variables & Data Types: Complete Guide
Variables are the backbone of any programming language, and shell scripting is no exception. They allow you to store data, reuse values, and build dynamic scripts that respond to different inputs and conditions.
In this guide, we'll cover everything you need to know about variables in Bash — from basic declaration to advanced techniques like command substitution and arithmetic operations.
Declaring Variables
In shell scripting, declaring a variable is straightforward. You assign a value using the = operator, and you retrieve it using the $ prefix.
1#!/bin/bash 2 3# String variable 4name="Tech3Space" 5 6# Integer variable (stored as string, but treated as number in arithmetic) 7age=25 8 9# Floating point (stored as string — arithmetic requires special tools) 10pi=3.14159 11 12# Accessing variables 13echo "Name: $name" 14echo "Age: $age" 15 16# Alternative syntax with curly braces (recommended) 17echo "Welcome, ${name}!" 18echo "In 5 years, you will be $((age + 5)) years old"
Output:
Name: Tech3Space
Age: 25
Welcome, Tech3Space!
In 5 years, you will be 30 years old
Important: There must be no spaces around the
=sign.name = "Tech3Space"will fail because the shell interpretsnameas a command.
Variable Naming Rules
Before you start naming variables, keep these rules in mind:
| Rule | Description | Example |
|---|---|---|
| Start with letter or underscore | Numbers at the beginning are invalid | _count=0 ✅ 1st_place=1 ❌ |
| Case-sensitive | Name and name are different variables | NAME="A" name="B" |
No spaces around = | The shell parses it as a command otherwise | x=10 ✅ x = 10 ❌ |
| Use only letters, numbers, underscores | Special characters will cause errors | user_name ✅ user-name ❌ |
| Convention: UPPERCASE for constants | Makes constants visually distinct | MAX_RETRIES=5 |
Good vs Bad Naming Examples
1#!/bin/bash 2 3# ✅ Good naming 4user_name="alice" 5backup_dir="/backups" 6MAX_CONNECTIONS=100 7_is_valid=true 8 9# ❌ Bad naming (will fail or cause issues) 10# 2nd_attempt="no" # Starts with number — ERROR 11# user-name="bob" # Hyphen treated as minus operator — ERROR 12# my var="hello" # Space in name — ERROR
Variable Types in Shell Scripting
Unlike languages such as Python or Java, Bash variables are untyped by default — everything is essentially a string. However, you can work with different data types using specific syntax and declarations.
1. String Variables
Strings are the default type. You can use single quotes, double quotes, or no quotes (for simple values).
1#!/bin/bash 2 3# Double quotes — allow variable expansion and command substitution 4website="Tech3Space" 5echo "Welcome to $website" # Output: Welcome to Tech3Space 6 7# Single quotes — literal strings, no expansion 8echo 'Welcome to $website' # Output: Welcome to $website 9 10# Concatenation 11greeting="Hello" 12name="World" 13message="$greeting, $name!" 14echo "$message" # Output: Hello, World!
2. Integer Variables
While all variables are technically strings, you can declare true integers using the declare command. This enables arithmetic operations without $(( )).
1#!/bin/bash 2 3# Standard integer (string storage) 4count=10 5 6# True integer declaration 7declare -i salary=5000 8salary=salary+500 # Works because of -i flag 9echo "New salary: $salary" # Output: New salary: 5500 10 11# Without declare -i, this would just reassign the string "salary+500"
3. Readonly Constants
Use readonly to create variables that cannot be modified after assignment. This is perfect for configuration values.
1#!/bin/bash 2 3readonly COMPANY="Tech3Space" 4readonly PI=3.14159 5readonly MAX_USERS=100 6 7echo "Company: $COMPANY" 8echo "Max users allowed: $MAX_USERS" 9 10# Attempting to change will cause an error 11# COMPANY="Other" # bash: COMPANY: readonly variable
4. Arrays
Arrays allow you to store multiple values in a single variable.
1#!/bin/bash 2 3# Indexed array 4colors=("red" "green" "blue" "yellow") 5 6echo "First color: ${colors[0]}" # red 7echo "All colors: ${colors[@]}" # red green blue yellow 8echo "Number of colors: ${#colors[@]}" # 4 9 10# Adding elements 11colors+=("purple") 12 13# Associative array (requires Bash 4+) 14declare -A user 15user[name]="Alice" 16user[role]="Admin" 17user[age]="30" 18 19echo "User: ${user[name]}, Role: ${user[role]}"
Special Variables
Bash provides built-in special variables that give you access to script metadata, arguments, and process information.
| Variable | Meaning | Example Output |
|---|---|---|
$0 | Script name | ./myscript.sh |
$1 to $9 | Positional arguments (1st to 9th) | arg1, arg2 |
$# | Number of arguments passed | 3 |
$* | All arguments as a single word | arg1 arg2 arg3 |
$@ | All arguments as separate strings | arg1 arg2 arg3 |
$? | Exit status of the last command | 0 (success), 1 (failure) |
$$ | Process ID (PID) of the current shell | 12345 |
$! | Process ID of the last background job | 12346 |
Practical Example: Using Special Variables
1#!/bin/bash 2 3# special_vars.sh — Demonstrates special variables 4 5echo "Script name: $0" 6echo "First argument: $1" 7echo "Second argument: $2" 8echo "Third argument: $3" 9echo "Total arguments: $#" 10echo "All arguments (\$@): $@" 11echo "All arguments (\$*): $*" 12echo "Current process ID: $$" 13 14# Check if arguments were provided 15if [ $# -eq 0 ]; then 16 echo "Warning: No arguments provided!" 17 exit 1 18fi 19 20echo "Last command exit status: $?"
Test it:
1chmod +x special_vars.sh 2./special_vars.sh apple banana cherry
Output:
Script name: ./special_vars.sh
First argument: apple
Second argument: banana
Third argument: cherry
Total arguments: 3
All arguments ($@): apple banana cherry
All arguments ($*): apple banana cherry
Current process ID: 15432
Last command exit status: 0
$@ vs $*: What's the Difference?
This is a subtle but important distinction:
1#!/bin/bash 2 3echo "Using \$@ (preserves quoting):" 4for arg in "$@"; do 5 echo " -> [$arg]" 6done 7 8echo "Using \$* (collapses to single word):" 9for arg in "$*"; do 10 echo " -> [$arg]" 11done
Run with: ./script.sh "hello world" foo
Output:
Using $@ (preserves quoting):
-> [hello world]
-> [foo]
Using $* (collapses to single word):
-> [hello world foo]
Best Practice: Use
"$@"when iterating over arguments — it preserves spaces within quoted arguments.
Environment Variables
Environment variables are system-wide variables that affect the behavior of running processes. They configure everything from your shell prompt to language settings and application credentials.
Common Environment Variables
1#!/bin/bash 2 3echo "=== Environment Variables ===" 4echo "User: $USER" 5echo "Home directory: $HOME" 6echo "Current shell: $SHELL" 7echo "Path: $PATH" 8echo "Hostname: $HOSTNAME" 9echo "Working directory: $PWD" 10echo "Language: $LANG" 11echo "Terminal: $TERM" 12echo "Editor: $EDITOR"
Creating Custom Environment Variables
Use export to make a variable available to child processes:
1#!/bin/bash 2 3# Local variable (only available in this script) 4local_var="I am local" 5 6# Environment variable (available to child processes) 7export API_KEY="sk-123456789" 8export APP_ENV="production" 9export DB_HOST="localhost" 10 11echo "Variables exported. Starting child process..." 12./child_script.sh
child_script.sh:
1#!/bin/bash 2echo "API_KEY from parent: $API_KEY" 3echo "APP_ENV from parent: $APP_ENV"
Persisting Environment Variables
To make environment variables permanent, add them to your shell configuration file:
1# Add to ~/.bashrc or ~/.bash_profile 2echo 'export MY_APP_TOKEN="abc123"' >> ~/.bashrc 3source ~/.bashrc
Command Substitution
Command substitution allows you to capture the output of a command and store it in a variable. This is one of the most powerful features of shell scripting.
Two Syntaxes
1#!/bin/bash 2 3# Legacy backtick syntax (still works but less preferred) 4date_now=`date` 5 6# Modern $() syntax (recommended — supports nesting) 7current_date=$(date +%Y-%m-%d) 8files_count=$(ls | wc -l) 9disk_usage=$(df -h / | awk 'NR==2 {print $5}') 10 11echo "Today's date: $current_date" 12echo "Files in directory: $files_count" 13echo "Root disk usage: $disk_usage"
Practical Examples
1#!/bin/bash 2 3# Get system information 4hostname=$(hostname) 5uptime_info=$(uptime -p) 6kernel=$(uname -r) 7 8echo "System: $hostname" 9echo "Uptime: $uptime_info" 10echo "Kernel: $kernel" 11 12# Find the latest file in a directory 13latest_file=$(ls -t /var/log/*.log | head -n 1) 14echo "Latest log file: $latest_file" 15 16# Count lines in a file 17if [ -f "data.txt" ]; then 18 line_count=$(wc -l < data.txt) 19 echo "data.txt has $line_count lines" 20fi
Nesting Command Substitution
The $() syntax shines when you need to nest commands:
1#!/bin/bash 2 3# Get the basename of the current directory 4current_dir=$(basename $(pwd)) 5echo "Current directory name: $current_dir" 6 7# Get IP address (cross-platform) 8ip_address=$(ip addr show | grep "inet " | head -n 1 | awk '{print $2}' | cut -d/ -f1) 9echo "IP Address: $ip_address"
Arithmetic Operations
Bash supports integer arithmetic natively. For floating-point calculations, you'll need external tools like bc or awk.
Integer Arithmetic with $(( ))
1#!/bin/bash 2 3a=10 4b=3 5 6echo "Addition: $((a + b))" # 13 7echo "Subtraction: $((a - b))" # 7 8echo "Multiplication: $((a * b))" # 30 9echo "Division: $((a / b))" # 3 (integer division) 10echo "Modulo: $((a % b))" # 1 11echo "Exponent: $((a ** 2))" # 100
Using let Command
1#!/bin/bash 2 3x=5 4y=2 5 6let result=x+y 7echo "Result: $result" # 7 8 9let x++ # Increment 10echo "Incremented x: $x" # 6 11 12let x*=3 # Multiply and assign 13echo "x multiplied by 3: $x" # 18
Using expr Command
1#!/bin/bash 2 3# expr is older but still useful in some contexts 4sum=$(expr 10 + 5) 5product=$(expr 10 \* 5) # * must be escaped 6 7echo "Sum: $sum" # 15 8echo "Product: $product" # 50
Floating-Point Arithmetic with bc
Bash doesn't support floating-point math natively. Use bc (basic calculator) for precise decimal calculations:
1#!/bin/bash 2 3# Simple float calculation 4result=$(echo "10.5 + 3.2" | bc) 5echo "10.5 + 3.2 = $result" # 13.7 6 7# Division with scale (decimal places) 8pi=$(echo "scale=10; 22 / 7" | bc) 9echo "Approximation of pi: $pi" # 3.1428571428 10 11# Using variables 12a=15.5 13b=2.5 14quotient=$(echo "scale=2; $a / $b" | bc) 15echo "$a / $b = $quotient" # 6.20 16 17# Square root 18sqrt=$(echo "scale=4; sqrt(625)" | bc) 19echo "Square root of 625: $sqrt" # 25.0000
Arithmetic Comparison in Conditionals
1#!/bin/bash 2 3score=85 4passing=60 5 6if [ $score -gt $passing ]; then 7 echo "Congratulations! You passed with $score." 8else 9 echo "You need $((passing - score)) more points to pass." 10fi 11 12# Using (( )) for arithmetic conditions 13count=0 14while ((count < 5)); do 15 echo "Count: $count" 16 ((count++)) 17done
Arithmetic Comparison Operators:
| Operator | Meaning |
|---|---|
-eq | Equal to |
-ne | Not equal to |
-gt | Greater than |
-ge | Greater than or equal |
-lt | Less than |
-le | Less than or equal |
Real-World Script: Putting It All Together
Here's a practical script that combines variables, special parameters, command substitution, and arithmetic:
1#!/bin/bash 2 3# backup_manager.sh — Creates timestamped backups with size checking 4 5# Configuration 6readonly BACKUP_DIR="/backups" 7readonly SOURCE_DIR="${1:-/var/www/html}" # Default to /var/www/html if no arg 8readonly MAX_BACKUP_SIZE_MB=500 9TIMESTAMP=$(date +%Y%m%d_%H%M%S) 10BACKUP_NAME="backup_${TIMESTAMP}.tar.gz" 11 12echo "=== Backup Manager ===" 13echo "Script: $0" 14echo "Source: $SOURCE_DIR" 15echo "Destination: $BACKUP_DIR" 16echo "Started at: $(date)" 17echo "" 18 19# Check if source exists 20if [ ! -d "$SOURCE_DIR" ]; then 21 echo "Error: Source directory does not exist!" 22 exit 1 23fi 24 25# Create backup directory if needed 26mkdir -p "$BACKUP_DIR" 27 28# Calculate source size 29source_size=$(du -sm "$SOURCE_DIR" | cut -f1) 30echo "Source size: ${source_size}MB" 31 32if [ "$source_size" -gt "$MAX_BACKUP_SIZE_MB" ]; then 33 echo "Warning: Source exceeds ${MAX_BACKUP_SIZE_MB}MB. Backup may take a while." 34fi 35 36# Create backup 37echo "Creating backup..." 38tar -czf "$BACKUP_DIR/$BACKUP_NAME" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" 39 40# Verify backup 41if [ $? -eq 0 ]; then 42 backup_size=$(du -h "$BACKUP_DIR/$BACKUP_NAME" | cut -f1) 43 echo "✅ Backup successful: $BACKUP_NAME ($backup_size)" 44 45 # Cleanup old backups (keep last 5) 46 cd "$BACKUP_DIR" || exit 47 backup_count=$(ls -1 backup_*.tar.gz 2>/dev/null | wc -l) 48 49 if [ "$backup_count" -gt 5 ]; then 50 echo "Cleaning up old backups..." 51 ls -t backup_*.tar.gz | tail -n +6 | xargs rm -f 52 fi 53else 54 echo "❌ Backup failed!" 55 exit 1 56fi 57 58echo "Process completed. PID was: $$"
Summary Cheat Sheet
1# Variable declaration 2name="value" # String 3declare -i num=10 # Integer 4readonly CONST="fixed" # Constant 5arr=(a b c) # Array 6 7# Accessing variables 8$name # Basic 9${name} # Safe (use with concatenation) 10${arr[0]} # Array element 11${#arr[@]} # Array length 12 13# Special variables 14$0 $1 $2 ... $@ $* $# $? $$ $! 15 16# Command substitution 17$(command) # Preferred 18`command` # Legacy 19 20# Arithmetic 21$((a + b)) # Integer math 22let x=y+z # Assignment 23expr 5 + 3 # Expression 24echo "scale=2; 10/3" | bc # Float math
Mastering variables is the foundation of writing powerful shell scripts. Once you're comfortable with declaring, accessing, and manipulating variables, you'll be ready to tackle control structures like conditionals and loops.
Continue your shell scripting journey with our next guide on conditionals and flow control!