source filename [arguments]
. filename [arguments]
The source command (also invoked as .) reads and executes commands
from a file in the current shell environment. This is a crucial distinction from running a
script normally - when you execute a script directly, it runs in a subshell and any variable changes,
function definitions, or directory changes are lost when the script completes. With source,
everything happens in your current shell session.
This makes source essential for configuration files, environment setup scripts,
shell function libraries, and any scenario where you need changes to persist in your current
shell. It's commonly used with .bashrc, .profile, Python virtual
environments, and custom configuration scripts.
# Edit your .bashrc
vim ~/.bashrc
# Add a new alias
# alias ll='ls -lah --color=auto'
# Reload the configuration in current shell
source ~/.bashrc
# Or using the dot notation (POSIX-compliant)
. ~/.bashrc
# Verify the alias is now available
ll /tmp
# After sourcing, the 'll' alias is immediately available
total 48K
drwxrwxrwt 12 root root 4.0K Dec 14 13:45 .
drwxr-xr-x 20 root root 4.0K Nov 28 09:12 ..
drwxrwxrwt 2 root root 4.0K Dec 14 08:00 .X11-unix
Without source, you'd need to open a new terminal or log out/in to see the changes. Sourcing applies them immediately to your current session.
# Create an environment configuration file
cat > ~/dev_environment.sh << 'EOF'
#!/bin/bash
# Development environment configuration
export JAVA_HOME="/usr/lib/jvm/java-11-openjdk"
export MAVEN_HOME="/opt/maven"
export PATH="$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH"
export DB_HOST="localhost"
export DB_PORT="5432"
export DB_NAME="devdb"
export LOG_LEVEL="DEBUG"
echo "Development environment loaded"
echo "JAVA_HOME: $JAVA_HOME"
echo "DB_HOST: $DB_HOST"
EOF
# Make it executable (optional for sourced scripts)
chmod +x ~/dev_environment.sh
# Source it to load variables
source ~/dev_environment.sh
# Verify variables are set
echo $JAVA_HOME
echo $PATH | tr ':' '\n' | grep java
Development environment loaded
JAVA_HOME: /usr/lib/jvm/java-11-openjdk
DB_HOST: localhost
/usr/lib/jvm/java-11-openjdk
/usr/lib/jvm/java-11-openjdk/bin
If you ran this as ./dev_environment.sh instead of sourcing it, the variables would only exist in the subshell and disappear immediately. Source makes them persist in your current session.
# Create a Python virtual environment
python3 -m venv myproject_env
# Activate it using source
source myproject_env/bin/activate
# Or using dot notation
. myproject_env/bin/activate
# Your prompt changes to show (myproject_env)
# Check which python is being used
which python
echo $VIRTUAL_ENV
# Install packages (will go into the virtual environment)
pip install requests
# When done, deactivate (this is a function defined by activate script)
deactivate
# After sourcing activate:
(myproject_env) user@host:~$ which python
/home/user/myproject_env/bin/python
(myproject_env) user@host:~$ echo $VIRTUAL_ENV
/home/user/myproject_env
# After deactivate:
user@host:~$ which python
/usr/bin/python
Running ./myproject_env/bin/activate WILL NOT WORK. The activate script must be sourced to modify your current shell's environment. This is why it's always source or .
# Create a function library
cat > ~/shell_functions.sh << 'EOF'
#!/bin/bash
# Shell Function Library
# Backup a file with timestamp
backup_file() {
if [ -z "$1" ]; then
echo "Usage: backup_file <filename>"
return 1
fi
if [ ! -f "$1" ]; then
echo "Error: File '$1' not found"
return 1
fi
local backup_name="${1}.backup.$(date +%Y%m%d_%H%M%S)"
cp "$1" "$backup_name"
echo "Backed up to: $backup_name"
}
# Create directory and cd into it
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Extract various archive formats
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.gz) tar xzf "$1" ;;
*.tar.bz2) tar xjf "$1" ;;
*.zip) unzip "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xf "$1" ;;
*) echo "Unknown archive format" ;;
esac
else
echo "File not found: $1"
fi
}
# Find process by name
findproc() {
ps aux | grep -i "$1" | grep -v grep
}
echo "Shell functions loaded: backup_file, mkcd, extract, findproc"
EOF
# Source the library
source ~/shell_functions.sh
# Now use the functions
backup_file /etc/hosts
mkcd ~/test_directory
findproc nginx
Shell functions loaded: backup_file, mkcd, extract, findproc
Backed up to: /etc/hosts.backup.20251214_134523
# mkcd creates directory and changes to it
user@host:~/test_directory$
# findproc shows matching processes
root 1234 0.0 0.1 12345 6789 ? Ss Dec13 0:05 nginx: master process
www-data 5678 0.0 0.2 23456 8901 ? S Dec13 0:12 nginx: worker process
Functions defined in a sourced script become available in your current shell. You can add source ~/shell_functions.sh to your .bashrc to load these functions automatically in every new shell.
# Create a configurable environment loader
cat > ~/load_env.sh << 'EOF'
#!/bin/bash
# Load environment based on argument
ENVIRONMENT="${1:-development}"
case "$ENVIRONMENT" in
production)
export DB_HOST="prod-db.example.com"
export DB_PORT="5432"
export LOG_LEVEL="ERROR"
export CACHE_ENABLED="true"
;;
staging)
export DB_HOST="staging-db.example.com"
export DB_PORT="5432"
export LOG_LEVEL="WARN"
export CACHE_ENABLED="true"
;;
development)
export DB_HOST="localhost"
export DB_PORT="5432"
export LOG_LEVEL="DEBUG"
export CACHE_ENABLED="false"
;;
*)
echo "Unknown environment: $ENVIRONMENT"
echo "Valid options: production, staging, development"
return 1
;;
esac
echo "Loaded $ENVIRONMENT environment"
echo " DB_HOST: $DB_HOST"
echo " LOG_LEVEL: $LOG_LEVEL"
EOF
# Source with different arguments
source ~/load_env.sh production
echo "---"
source ~/load_env.sh staging
echo "---"
source ~/load_env.sh development
Loaded production environment
DB_HOST: prod-db.example.com
LOG_LEVEL: ERROR
---
Loaded staging environment
DB_HOST: staging-db.example.com
LOG_LEVEL: WARN
---
Loaded development environment
DB_HOST: localhost
LOG_LEVEL: DEBUG
Arguments allow you to make sourced scripts more flexible. This pattern is commonly used for environment switching in development workflows.
# Safe sourcing with existence check
if [ -f ~/.custom_config ]; then
source ~/.custom_config
echo "Custom configuration loaded"
else
echo "Custom configuration not found, using defaults"
fi
# Source with error handling
if source ~/app_config.sh 2>/dev/null; then
echo "Application config loaded successfully"
else
echo "Failed to load application config"
exit 1
fi
# Multiple configuration files with fallback
for config in ~/.config/app/config.sh /etc/app/config.sh; do
if [ -f "$config" ]; then
source "$config"
echo "Loaded configuration from: $config"
break
fi
done
# Function for safe sourcing
safe_source() {
local file="$1"
if [ ! -f "$file" ]; then
echo "Warning: File not found: $file"
return 1
fi
if source "$file"; then
echo "Successfully sourced: $file"
return 0
else
echo "Error sourcing: $file"
return 1
fi
}
# Use the function
safe_source ~/my_config.sh
Custom configuration not found, using defaults
Application config loaded successfully
Loaded configuration from: /etc/app/config.sh
Successfully sourced: ~/my_config.sh
Always check if a file exists before sourcing it in scripts. A missing configuration file can cause the script to fail, and sourcing a non-existent file will throw an error that might exit your current shell.
# Create a test script
cat > test_script.sh << 'EOF'
#!/bin/bash
# This script demonstrates source vs execute
export TEST_VAR="Hello from script"
cd /tmp
echo "Inside script:"
echo " TEST_VAR: $TEST_VAR"
echo " PWD: $PWD"
EOF
chmod +x test_script.sh
# Before running anything
echo "Before running script:"
echo " TEST_VAR: $TEST_VAR"
echo " PWD: $PWD"
echo ""
# Method 1: Execute normally (creates subshell)
echo "=== Executing normally (./test_script.sh) ==="
./test_script.sh
echo "After normal execution:"
echo " TEST_VAR: $TEST_VAR" # Empty!
echo " PWD: $PWD" # Still in original directory
echo ""
# Method 2: Source the script (runs in current shell)
echo "=== Sourcing (source test_script.sh) ==="
source test_script.sh
echo "After sourcing:"
echo " TEST_VAR: $TEST_VAR" # Variable is set!
echo " PWD: $PWD" # Directory changed!
# Clean up
cd -
unset TEST_VAR
Before running script:
TEST_VAR:
PWD: /home/user
=== Executing normally (./test_script.sh) ===
Inside script:
TEST_VAR: Hello from script
PWD: /tmp
After normal execution:
TEST_VAR:
PWD: /home/user
=== Sourcing (source test_script.sh) ===
Inside script:
TEST_VAR: Hello from script
PWD: /tmp
After sourcing:
TEST_VAR: Hello from script
PWD: /tmp
This clearly shows that normal execution creates a subshell where changes are isolated, while source executes in the current shell where changes persist. This is fundamental to understanding when to use each approach.
# Create project configuration system
mkdir -p ~/project_configs
# Create different config files
cat > ~/project_configs/web_config.sh << 'EOF'
export PROJECT_TYPE="web"
export PORT="8080"
export BUILD_CMD="npm run build"
export START_CMD="npm start"
alias proj-start='npm start'
alias proj-build='npm run build'
echo "Web project configuration loaded"
EOF
cat > ~/project_configs/python_config.sh << 'EOF'
export PROJECT_TYPE="python"
export VENV_PATH="./venv"
export BUILD_CMD="python setup.py build"
export START_CMD="python main.py"
alias proj-start='source venv/bin/activate && python main.py'
alias proj-build='python setup.py build'
echo "Python project configuration loaded"
EOF
cat > ~/project_configs/java_config.sh << 'EOF'
export PROJECT_TYPE="java"
export MAVEN_OPTS="-Xmx2048m"
export BUILD_CMD="mvn clean install"
export START_CMD="java -jar target/app.jar"
alias proj-start='java -jar target/app.jar'
alias proj-build='mvn clean install'
echo "Java project configuration loaded"
EOF
# Create interactive loader
cat > ~/load_project.sh << 'EOF'
#!/bin/bash
echo "Select Project Configuration:"
echo "1) Web (Node.js)"
echo "2) Python"
echo "3) Java"
echo -n "Enter choice [1-3]: "
read choice
case $choice in
1)
source ~/project_configs/web_config.sh
;;
2)
source ~/project_configs/python_config.sh
;;
3)
source ~/project_configs/java_config.sh
;;
*)
echo "Invalid choice"
return 1
;;
esac
echo "Project type: $PROJECT_TYPE"
echo "Build command: $BUILD_CMD"
echo "Start command: $START_CMD"
EOF
# Use it interactively
source ~/load_project.sh
Select Project Configuration:
1) Web (Node.js)
2) Python
3) Java
Enter choice [1-3]: 2
Python project configuration loaded
Project type: python
Build command: python setup.py build
Start command: python main.py
# Now the aliases are available:
$ proj-start
# Would activate venv and run main.py
This pattern is useful for developers who work on multiple types of projects and need to quickly switch configurations. The sourced aliases and environment variables persist in the current shell.
# Create modular configuration system
mkdir -p ~/deploy_scripts/{base,database,webserver,monitoring}
# Base configuration
cat > ~/deploy_scripts/base/common.sh << 'EOF'
#!/bin/bash
LOG_DIR="/var/log/deployment"
DEPLOY_USER="deployer"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_DIR/deploy_$TIMESTAMP.log"
}
EOF
# Database configuration
cat > ~/deploy_scripts/database/postgres.sh << 'EOF'
#!/bin/bash
DB_HOST="localhost"
DB_PORT="5432"
DB_NAME="production"
DB_BACKUP_DIR="/backups/postgres"
backup_database() {
log_message "Starting database backup"
pg_dump -h $DB_HOST -p $DB_PORT $DB_NAME > "$DB_BACKUP_DIR/backup_$TIMESTAMP.sql"
log_message "Database backup complete"
}
EOF
# Web server configuration
cat > ~/deploy_scripts/webserver/nginx.sh << 'EOF'
#!/bin/bash
NGINX_CONFIG="/etc/nginx/sites-available"
WEB_ROOT="/var/www/html"
restart_webserver() {
log_message "Restarting nginx"
sudo systemctl restart nginx
log_message "Nginx restarted"
}
EOF
# Main deployment script that sources everything
cat > ~/deploy_scripts/deploy.sh << 'EOF'
#!/bin/bash
# Source all configuration modules
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/base/common.sh"
source "$SCRIPT_DIR/database/postgres.sh"
source "$SCRIPT_DIR/webserver/nginx.sh"
log_message "=== Starting Deployment ==="
log_message "Deployment user: $DEPLOY_USER"
# Now we can use functions from all sourced files
backup_database
# ... deploy application code ...
restart_webserver
log_message "=== Deployment Complete ==="
EOF
chmod +x ~/deploy_scripts/deploy.sh
# Run the deployment
~/deploy_scripts/deploy.sh
[2025-12-14 14:30:15] === Starting Deployment ===
[2025-12-14 14:30:15] Deployment user: deployer
[2025-12-14 14:30:15] Starting database backup
[2025-12-14 14:30:18] Database backup complete
[2025-12-14 14:30:18] Restarting nginx
[2025-12-14 14:30:20] Nginx restarted
[2025-12-14 14:30:20] === Deployment Complete ===
This modular approach makes maintenance easier. Database configs go in one file, web server configs in another. When you need to update database settings, you only modify postgres.sh. The main script sources all modules to access their functions and variables.
# BAD: Script that uses exit (DO NOT SOURCE THIS)
cat > ~/bad_script.sh << 'EOF'
#!/bin/bash
if [ ! -f "/some/required/file" ]; then
echo "Required file not found"
exit 1 # THIS WILL CLOSE YOUR SHELL IF SOURCED!
fi
EOF
# GOOD: Script that uses return (safe to source)
cat > ~/good_script.sh << 'EOF'
#!/bin/bash
if [ ! -f "/some/required/file" ]; then
echo "Required file not found"
return 1 # Safe - only exits the sourced script
fi
echo "File found, continuing..."
EOF
# Create a smart script that works both ways
cat > ~/smart_script.sh << 'EOF'
#!/bin/bash
# Detect if being sourced or executed
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
# Being executed, not sourced - can use exit
SOURCED=false
else
# Being sourced - must use return
SOURCED=true
fi
# Function to exit appropriately
smart_exit() {
local exit_code=${1:-0}
if [ "$SOURCED" = true ]; then
return $exit_code
else
exit $exit_code
fi
}
# Validation check
if [ ! -d "/tmp" ]; then
echo "Error: /tmp directory not found"
smart_exit 1
fi
# Set some variables
export SMART_VAR="Loaded successfully"
echo "Smart script loaded correctly"
smart_exit 0
EOF
chmod +x ~/smart_script.sh
# Test it both ways
echo "=== Test 1: Sourcing the smart script ==="
source ~/smart_script.sh
echo "Still here! SMART_VAR: $SMART_VAR"
echo ""
echo "=== Test 2: Executing the smart script ==="
./~/smart_script.sh
echo "Still here! SMART_VAR: $SMART_VAR" # Will be empty
echo ""
# Demonstrate what happens with bad script
echo "=== WARNING: Do NOT run this - it would close your shell ==="
echo "# source ~/bad_script.sh # Would close your terminal!"
echo "=== Safe to source ==="
source ~/good_script.sh || echo "Returned with error code, but shell still open"
=== Test 1: Sourcing the smart script ===
Smart script loaded correctly
Still here! SMART_VAR: Loaded successfully
=== Test 2: Executing the smart script ===
Smart script loaded correctly
Still here! SMART_VAR:
=== WARNING: Do NOT run this - it would close your shell ===
# source ~/bad_script.sh # Would close your terminal!
=== Safe to source ===
Required file not found
Returned with error code, but shell still open
NEVER use 'exit' in a script that will be sourced! It will close your current shell session. Always use 'return' instead. The smart_exit pattern shown here detects whether the script is sourced or executed and behaves appropriately.
. filename
[ -f file.sh ] && source file.sh. This prevents errors from missing
configuration files and makes your scripts more robust.
source ~/my_functions.sh. This loads them automatically when
you open a new terminal.
source ./config.sh or source ~/config.sh. This makes it obvious
which file you're sourcing and prevents unexpected behavior from PATH search order.
[ "${BASH_SOURCE[0]}" != "${0}" ]. This
allows you to write scripts that work correctly whether sourced or executed directly.
[ -n "$GUARD_VAR" ] && return at the
top of sourced files.