Bash (Bourne Again SHell) is a Unix shell and command language, developed for the GNU Project as a free software replacement for the Bourne shell (sh). It's the default command-line interpreter on most Linux distributions and macOS (older versions), providing a powerful interface for interacting with the operating system.
Bash allows users to execute commands, navigate the file system, manage files and processes, and automate tasks through shell scripting.
You access Bash through a terminal emulator.
# Check your current shell
echo $SHELL
# Output will likely be /bin/bash or /bin/zsh (zsh is default on newer macOS)
# If not bash, you can usually start bash by typing:
bash
Fundamental commands for getting around and interacting with your system.
pwd
# Example Output: /home/yourusername
ls # List contents of current directory
ls -l # Long format (permissions, owner, size, date)
ls -a # List all files, including hidden ones (starting with .)
ls -lh # Long format, human-readable sizes (KB, MB, GB)
ls /etc # List contents of a specific directory
cd Documents # Move into 'Documents'
cd .. # Move up one directory
cd ~ # Move to your home directory
cd / # Move to the root directory
cd - # Move to the previous directory
history
history 10 # Last 10 commands
!50 # Re-execute command number 50 from history
!! # Re-execute last command
man ls # Press 'q' to quit
echo "Hello, Bash!"
Creating, moving, copying, and deleting files and folders.
mkdir my_new_folder
mkdir -p projects/my_app # Creates parent directories if they don't exist
rmdir my_empty_folder
touch new_document.txt
touch script.sh
cp file1.txt file2.txt # Copy file1.txt to file2.txt in same directory
cp image.jpg /home/user/Pictures/ # Copy to another directory
cp -r my_folder /backup/ # Copy directory recursively
mv old_name.txt new_name.txt # Rename a file
mv document.pdf /archive/ # Move file to /archive/
rm unwanted_file.txt
rm -i important_file.txt # Prompt before deleting
rm -r my_folder # Remove a directory and its contents recursively (USE WITH CAUTION!)
rm -f stubborn_file.txt # Force remove (no prompt)
rm -rf very_critical_folder # Force remove recursively (EXTREME CAUTION: Irreversible!)
cat my_document.txt
cat file1.txt file2.txt # Concatenate and display both files
less large_log_file.log # Press Space to scroll, 'q' to quit
head -n 5 document.txt # Show first 5 lines
tail -n 10 server.log # Show last 10 lines
tail -f /var/log/syslog # Follow a log file in real-time (Ctrl+C to exit)
Linux is a multi-user system, and file permissions control who can read, write, or execute files and directories.
ls -l my_script.sh
# Example Output: -rwxr-xr-- 1 user group 1234 Jul 19 10:00 my_script.sh
Uses symbolic mode (r, w, x, u, g, o, a) or octal (numeric) mode.
chmod 755 my_script.sh # Owner: rwx (7), Group: r-x (5), Others: r-x (5)
# Visual: -rwxr-xr-x
chmod 644 my_document.txt # Owner: rw- (6), Group: r-- (4), Others: r-- (4)
# Visual: -rw-r--r--
chmod +x my_script.sh # Add execute permission for all (equivalent to 755 if it was 644)
Requires `sudo` (superuser privileges).
sudo chown youruser:yourgroup myfile.txt
sudo chown -R youruser:yourgroup myfolder # Recursively change for folder contents
sudo chgrp newgroup myfile.txt
Control where command output goes and where input comes from.
ls -l > file_list.txt # Send output of ls -l to file_list.txt (overwrites if exists)
echo "Another line." >> file_list.txt # Append text to file_list.txt
cat < input.txt # Read input for cat command from input.txt
ls non_existent_file 2> error.log # Send error messages to error.log
command > output.log 2>&1 # Old syntax: redirect stderr to same place as stdout
command &> output.log # Modern syntax: redirect both to output.log
The pipe operator (`|`) sends the standard output of one command as the standard input to another command. Filters are commands that process data (like `grep`, `awk`, `sed`).
ls -l | less # Pipe output of ls -l to less for paginated viewing
df -h | grep "/dev/sda" # Filter disk usage for a specific device
cat /etc/passwd | grep "root" # Find lines with "root" in passwd file
grep -i "error" /var/log/syslog # Case-insensitive search for "error" in syslog
cat /etc/passwd | awk -F: '{print $1}' # Print first field (username) from passwd file, using ':' as delimiter
cat file.txt | sed 's/old_text/new_text/g' # Replace all occurrences of old_text with new_text
sed -i 's/foo/bar/g' myfile.txt # In-place edit: replace foo with bar directly in myfile.txt
ls -l | sort -k 5 -nr # Sort files by size (5th column), numeric, reverse
cat words.txt | sort | uniq -c # Count unique lines after sorting
Store values in memory for later use. Variables are case-sensitive.
NAME="Alice"
echo "Hello, $NAME!" # Output: Hello, Alice!
MY_PATH="/home/user/documents"
echo $MY_PATH
echo $PATH # Shows directories where executables are searched
echo $HOME # Your home directory
echo $USER # Your current username
export MY_VAR="some_value" # Make MY_VAR available to child processes
# In a script named 'myscript.sh'
echo "Script name: $0"
echo "First argument: $1"
echo "Number of arguments: $#"
echo "All arguments: $@"
Bash can perform basic arithmetic using `(( ))` or `expr`.
NUM1=10
NUM2=5
RESULT=$(( NUM1 + NUM2 ))
echo "Sum: $RESULT" # Output: Sum: 15
# Increment/Decrement
COUNT=1
((COUNT++))
echo "Count: $COUNT" # Output: Count: 2
# Operations: +, -, *, /, % (modulus)
echo $(( 10 * 3 )) # Output: 30
echo $(( 10 / 3 )) # Output: 3 (integer division)
RESULT=$(expr 10 + 5)
echo "Sum: $RESULT" # Output: Sum: 15
Allows the output of a command to be used as an argument to another command or assigned to a variable.
CURRENT_DATE=`date`
echo "Today is: $CURRENT_DATE"
FILE_COUNT=`ls -l | wc -l` # Count lines (files) in current directory
echo "Number of files: $((FILE_COUNT - 1))" # Subtract 1 for total line from `ls -l` which includes total.
HOSTNAME=$(hostname)
echo "Running on: $HOSTNAME"
LIST_FILES=$(ls)
echo "Files: $LIST_FILES"
Used to control how Bash interprets special characters.
echo '$PATH' # Output: $PATH (literal '$PATH')
echo 'This is a $ string with spaces.'
NAME="Bob"
echo "Hello, $NAME!" # Output: Hello, Bob! ($NAME is expanded)
echo "Files in current directory: $(ls)" # Command substitution works
echo "My * files" # Output: My * files (* is not expanded as wildcard)
echo "I have \$5." # Output: I have $5.
echo This is a \*wildcard. # Output: This is a *wildcard.
Automate tasks by writing a series of commands in a file.
# my_script.sh
#!/bin/bash
# This is my first Bash script.
echo "--- Script Start ---"
echo "Current directory: $(pwd)"
echo "Listing files:"
ls -lh
echo "--- Script End ---"
You need to give the script execute permissions.
chmod +x my_script.sh
./my_script.sh
Or, if it's in your PATH:
my_script.sh
Execute code blocks based on conditions. The most common form uses `if`, `elif` (else if), and `else` with `[[ ]]` for evaluations.
#!/bin/bash
echo "Enter a number:"
read NUM
if [[ $NUM -gt 10 ]]; then
echo "$NUM is greater than 10."
fi
#!/bin/bash
echo "Is it sunny? (yes/no)"
read WEATHER
if [[ "$WEATHER" == "yes" ]]; then
echo "It's a good day for a walk!"
else
echo "Stay indoors."
fi
#!/bin/bash
echo "Enter your age:"
read AGE
if [[ $AGE -lt 18 ]]; then
echo "You are a minor."
elif [[ $AGE -ge 18 && $AGE -lt 65 ]]; then
echo "You are an adult."
else
echo "You are a senior citizen."
fi
if [[ -f "config.txt" ]]; then
echo "Config file found."
fi
Repeat a block of code multiple times.
#!/bin/bash
for FRUIT in apple banana cherry; do
echo "I like $FRUIT."
done
# Loop through numbers
for i in {1..5}; do
echo "Count: $i"
done
# Loop through command output
for FILE in $(ls *.txt); do
echo "Processing $FILE"
done
#!/bin/bash
for (( i=0; i<3; i++ )); do
echo "C-style loop: $i"
done
#!/bin/bash
COUNT=0
while [[ $COUNT -lt 3 ]]; do
echo "While loop: $COUNT"
((COUNT++)) # Increment COUNT
done
Group commands into reusable blocks of code.
#!/bin/bash
greet_user() {
echo "Hello, $1!" # $1 refers to the first argument passed to the function
}
# Call the function
greet_user "Alice" # Output: Hello, Alice!
greet_user "Bob" # Output: Hello, Bob!
#!/bin/bash
calculate_sum() {
local SUM=$(( $1 + $2 )) # 'local' makes variable scoped to function
echo $SUM # Print the sum
}
RESULT=$(calculate_sum 10 20)
echo "The sum is: $RESULT" # Output: The sum is: 30
Scripts can take inputs directly from the command line.
#!/bin/bash
echo "Script name: $0"
echo "Number of arguments: $#"
if [[ $# -eq 0 ]]; then
echo "No arguments provided."
exit 1 # Exit with error code
fi
echo "First argument: $1"
echo "All arguments (separate):"
for arg in "$@"; do
echo "- $arg"
done
chmod +x process_args.sh
./process_args.sh hello world 123
Example Output:
Script name: ./process_args.sh
Number of arguments: 3
First argument: hello
All arguments (separate):
- hello
- world
- 123
Making scripts robust and finding issues.
ls non_existent_file
echo "Exit status: $?" # Will be non-zero (e.g., 1 or 2)
ls /home
echo "Exit status: $?" # Will be 0
#!/bin/bash
set -e # Script will stop here if 'ls non_existent_file' fails
echo "Starting script..."
ls non_existent_file
echo "This line will not execute if previous command fails."
#!/bin/bash
set -x # Turn on debugging output
echo "Debugging this script."
VAR="test"
ls $VAR
set +x # Turn off debugging output
echo "Debugging finished."
#!/bin/bash
cleanup() {
echo "Cleaning up temporary files..."
rm -f /tmp/my_temp_file.txt
}
trap cleanup EXIT # Run cleanup function when script exits
trap "echo 'Ctrl+C detected! Exiting...'; cleanup; exit 1" INT # Handle Ctrl+C (Interrupt)
echo "Creating temp file..."
touch /tmp/my_temp_file.txt
echo "Running for 10 seconds. Press Ctrl+C to test trap."
sleep 10
echo "Script finished normally."
Manage commands running in the foreground or background.
sleep 60 # Press Ctrl+Z
# Output: [1]+ Stopped sleep 60
jobs
# Output: [1]+ Stopped sleep 60
bg %1 # Send job 1 to background
# Output: [1]+ sleep 60 &
fg %1
./my_long_script.sh & # Runs in background, shell is free
nohup ./my_long_running_process.sh &
Regex is a powerful language for pattern matching in text. Bash itself has limited direct regex support, but many common CLI tools leverage it.
grep "^start" myfile.txt # Lines starting with "start"
grep "end$" myfile.txt # Lines ending with "end"
grep -E "[0-9]{3}-[0-9]{3}-[0-9]{4}" contacts.txt # Phone numbers (e.g., 123-456-7890)
grep -P "email:\s(\S+@\S+\.\S+)" user_data.txt # Perl-compatible regex for email
sed -E 's/([0-9]{3})(-)([0-9]{3})/\1.\3/g' phone_numbers.txt # Replace hyphens with dots in phone numbers
sed '/^#/d' config.txt # Delete lines starting with '#' (comments)
awk '/^user/ {print $1, $NF}' /etc/passwd # Print username and shell for lines starting with "user"
if [[ "hello world" =~ "world" ]]; then
echo "Pattern found."
fi
EMAIL="user@example.com"
if [[ "$EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "Valid email format."
else
echo "Invalid email format."
fi
Improve productivity by creating shortcuts and reusing commands.
alias ll='ls -lh' # Make 'll' run 'ls -lh'
alias up='sudo apt update && sudo apt upgrade -y' # Shortcut for updating system
# To make aliases permanent, add them to ~/.bashrc and source it:
# echo "alias ll='ls -lh'" >> ~/.bashrc
# source ~/.bashrc
history # Show all commands
history | grep "ssh" # Find past SSH commands
!ls # Run the last command starting with 'ls'
!! # Run the very last command again
!$` # Access the last argument of the previous command
Beyond the basics, Bash offers more powerful features and best practices for writing robust scripts.
FILENAME="My Document.txt"
# WRONG: rm $FILENAME (will try to rm "My" and "Document.txt")
rm "$FILENAME" # CORRECT
echo "Error: File not found." >&2
LOG_FILE="/var/log/my_script.log"
log_message() {
echo "$(date): $1" | tee -a "$LOG_FILE"
}
log_message "Script started."
Bash is an incredibly powerful and versatile tool. While the initial learning curve might seem steep, mastering Bash commands and scripting will dramatically improve your efficiency in managing Linux/Unix systems, automating tasks, and performing complex operations. Embrace the command line, practice regularly, and unlock a new level of control over your computing environment.