How to Check if a Directory Exists in Bash Shell Scripts
Checking whether a directory exists is one of the most common tasks in Bash scripting. Whether you're creating backup scripts, setting up application environments, or managing file operations, verifying directory existence prevents errors and ensures your scripts run smoothly. Bash provides several ways to test for directory existence, each with specific use cases and advantages.
Prerequisites
You'll need basic familiarity with Bash scripting and command-line operations. A Unix-like system (Linux, macOS, or WSL) is required to test the examples.
The Standard Test Command Approach
The most common and portable method uses the test
command with the -d
flag:
#!/bin/bash
directory="/home/user/documents"
if [ -d "$directory" ]; then
echo "Directory $directory exists"
else
echo "Directory $directory does not exist"
fi
The -d
test returns true if the specified path exists and is a directory. Always quote your variables to handle paths with spaces correctly.
Using the Double Bracket Syntax
Bash provides enhanced test syntax with double brackets, which offers better readability and additional features:
#!/bin/bash
check_directory() {
local dir_path="$1"
if [[ -d "$dir_path" ]]; then
echo "✓ Directory exists: $dir_path"
return 0
else
echo "✗ Directory not found: $dir_path"
return 1
fi
}
# Test multiple directories
directories=(
"/tmp"
"/home/user/projects"
"/nonexistent/path"
"$HOME/Documents"
)
for dir in "${directories[@]}"; do
check_directory "$dir"
done
The double bracket syntax [[ ]]
is more robust than single brackets and handles special characters better.
Combining Existence with Permission Checks
Often you need to verify not just that a directory exists, but that you can actually use it:
#!/bin/bash
validate_directory() {
local dir_path="$1"
local operation="$2" # read, write, or execute
# First check if directory exists
if [[ ! -d "$dir_path" ]]; then
echo "Error: Directory does not exist: $dir_path"
return 1
fi
# Check specific permissions
case "$operation" in
"read")
if [[ -r "$dir_path" ]]; then
echo "✓ Directory is readable: $dir_path"
return 0
else
echo "✗ Directory is not readable: $dir_path"
return 1
fi
;;
"write")
if [[ -w "$dir_path" ]]; then
echo "✓ Directory is writable: $dir_path"
return 0
else
echo "✗ Directory is not writable: $dir_path"
return 1
fi
;;
"execute")
if [[ -x "$dir_path" ]]; then
echo "✓ Directory is executable: $dir_path"
return 0
else
echo "✗ Directory is not executable: $dir_path"
return 1
fi
;;
*)
echo "Error: Invalid operation. Use 'read', 'write', or 'execute'"
return 1
;;
esac
}
# Example usage
project_dir="/home/user/my_project"
validate_directory "$project_dir" "read"
validate_directory "$project_dir" "write"
This approach ensures your script can actually perform the intended operations on the directory.
Creating Directories with Existence Checks
A common pattern is checking for directory existence and creating it if necessary:
#!/bin/bash
ensure_directory() {
local dir_path="$1"
local permissions="$2" # optional, defaults to 755
# Set default permissions if not provided
permissions="${permissions:-755}"
if [[ -d "$dir_path" ]]; then
echo "Directory already exists: $dir_path"
return 0
else
echo "Creating directory: $dir_path"
if mkdir -p "$dir_path"; then
chmod "$permissions" "$dir_path"
echo "✓ Directory created successfully: $dir_path"
return 0
else
echo "✗ Failed to create directory: $dir_path"
return 1
fi
fi
}
# Example: Setting up project directory structure
project_root="/home/user/new_project"
directories=(
"$project_root"
"$project_root/src"
"$project_root/tests"
"$project_root/docs"
"$project_root/logs"
)
for dir in "${directories[@]}"; do
ensure_directory "$dir" "755"
done
# Create a restricted directory
ensure_directory "$project_root/private" "700"
The mkdir -p
command creates parent directories as needed, making this approach very reliable.
Handling Edge Cases and Special Paths
Real-world directory checking needs to handle various edge cases:
#!/bin/bash
robust_directory_check() {
local dir_path="$1"
# Handle empty input
if [[ -z "$dir_path" ]]; then
echo "Error: No directory path provided"
return 1
fi
# Expand tilde and variables
dir_path="${dir_path/#\~/$HOME}"
dir_path=$(eval echo "$dir_path")
# Check if path exists and is actually a directory
if [[ -e "$dir_path" ]]; then
if [[ -d "$dir_path" ]]; then
echo "✓ Valid directory: $dir_path"
return 0
else
echo "✗ Path exists but is not a directory: $dir_path"
return 2
fi
else
echo "✗ Path does not exist: $dir_path"
return 1
fi
}
# Test with various path formats
test_paths=(
"~/Documents"
'$HOME/Desktop'
"/tmp"
"/tmp/some_file.txt" # This might be a file, not directory
"" # Empty path
"/nonexistent/path"
)
for path in "${test_paths[@]}"; do
echo "Testing: '$path'"
robust_directory_check "$path"
echo "Return code: $?"
echo "---"
done
This function handles variable expansion, empty inputs, and distinguishes between non-existent paths and paths that exist but aren't directories.
Practical Application: Backup Script
Here's a real-world example that demonstrates directory checking in a backup script:
#!/bin/bash
create_backup() {
local source_dir="$1"
local backup_root="$2"
# Validate source directory
if [[ ! -d "$source_dir" ]]; then
echo "Error: Source directory does not exist: $source_dir"
return 1
fi
if [[ ! -r "$source_dir" ]]; then
echo "Error: Cannot read source directory: $source_dir"
return 1
fi
# Create backup directory structure
backup_date=$(date +"%Y-%m-%d_%H-%M-%S")
backup_dir="$backup_root/backup_$backup_date"
# Ensure backup root exists
if [[ ! -d "$backup_root" ]]; then
echo "Creating backup root directory: $backup_root"
mkdir -p "$backup_root" || {
echo "Error: Failed to create backup root: $backup_root"
return 1
}
fi
# Check if backup root is writable
if [[ ! -w "$backup_root" ]]; then
echo "Error: Backup root is not writable: $backup_root"
return 1
fi
# Create specific backup directory
if mkdir "$backup_dir"; then
echo "Created backup directory: $backup_dir"
else
echo "Error: Failed to create backup directory: $backup_dir"
return 1
fi
# Perform the backup
echo "Starting backup from $source_dir to $backup_dir"
if cp -r "$source_dir"/* "$backup_dir"/; then
echo "✓ Backup completed successfully"
echo "Backup location: $backup_dir"
return 0
else
echo "✗ Backup failed"
return 1
fi
}
# Example usage
source_directory="$HOME/important_docs"
backup_location="/backups/documents"
create_backup "$source_directory" "$backup_location"
This backup script demonstrates comprehensive directory validation before performing file operations.
Directory Checking in Loops and Conditionals
When processing multiple directories, combine existence checks with other operations:
#!/bin/bash
process_project_directories() {
local base_dir="$1"
# Check if base directory exists
if [[ ! -d "$base_dir" ]]; then
echo "Base directory not found: $base_dir"
return 1
fi
# Process each subdirectory
for project in "$base_dir"/*; do
# Check if it's actually a directory
if [[ -d "$project" ]]; then
project_name=$(basename "$project")
echo "Processing project: $project_name"
# Check for specific subdirectories
if [[ -d "$project/src" ]]; then
echo " ✓ Source directory found"
src_files=$(find "$project/src" -name "*.py" | wc -l)
echo " Python files: $src_files"
else
echo " ✗ No source directory"
fi
if [[ -d "$project/tests" ]]; then
echo " ✓ Tests directory found"
else
echo " ⚠ No tests directory"
fi
# Check if logs directory exists, create if needed
if [[ ! -d "$project/logs" ]]; then
mkdir "$project/logs"
echo " Created logs directory"
fi
echo "---"
fi
done
}
# Process all projects in workspace
workspace="/home/user/workspace"
process_project_directories "$workspace"
This example shows how to combine directory existence checks with file processing and directory creation.
Using Functions for Reusable Directory Operations
Create utility functions that you can reuse across multiple scripts:
#!/bin/bash
# Utility functions for directory operations
dir_exists() {
[[ -d "$1" ]]
}
dir_is_readable() {
[[ -d "$1" && -r "$1" ]]
}
dir_is_writable() {
[[ -d "$1" && -w "$1" ]]
}
dir_is_empty() {
[[ -d "$1" && -z "$(ls -A "$1")" ]]
}
ensure_dir() {
local dir_path="$1"
local permissions="${2:-755}"
if ! dir_exists "$dir_path"; then
mkdir -p "$dir_path" && chmod "$permissions" "$dir_path"
fi
}
# Example usage in a deployment script
app_name="myapp"
app_dirs=(
"/var/log/$app_name"
"/var/run/$app_name"
"/etc/$app_name"
"/opt/$app_name"
)
echo "Setting up application directories for $app_name"
for dir in "${app_dirs[@]}"; do
if ensure_dir "$dir"; then
echo "✓ Directory ready: $dir"
else
echo "✗ Failed to setup: $dir"
exit 1
fi
done
echo "All directories configured successfully"
These utility functions make your scripts more readable and maintainable.
Next Steps
You now have multiple approaches for checking directory existence in Bash scripts. Consider exploring advanced file system operations like monitoring directory changes with inotify
, implementing recursive directory processing, or building more sophisticated backup and synchronization scripts. You might also want to look into using these techniques with configuration management tools or deployment automation.
Good luck with your directory management scripts!
Found an issue?