File Test Operators
File test operators are essential for any script that works with files and directories. They let you check if files exist, verify permissions, determine file types, and compare files. These operators are used constantly in real-world shell scripts for validation and conditional logic!
Existence and Permission Tests
The most common file tests check whether files exist and what permissions they have. These are the foundation of defensive scripting!
Click Run to execute your code
Existence Operators
| Operator | Description | Example |
|---|---|---|
-e |
File exists (any type) | [[ -e "$file" ]] |
-f |
Exists and is a regular file | [[ -f "$file" ]] |
-d |
Exists and is a directory | [[ -d "$dir" ]] |
-L or -h |
Exists and is a symbolic link | [[ -L "$link" ]] |
Permission Operators
| Operator | Description | Example |
|---|---|---|
-r |
File is readable | [[ -r "$file" ]] |
-w |
File is writable | [[ -w "$file" ]] |
-x |
File is executable | [[ -x "$script" ]] |
-s |
File exists and size > 0 | [[ -s "$file" ]] |
[[ -f "$file" ]] && cat "$file" to avoid errors when the file doesn't exist.
File Type Tests
Beyond regular files and directories, Unix has several special file types. These operators help you identify them.
Click Run to execute your code
| Operator | Description | Common Examples |
|---|---|---|
-f |
Regular file | Scripts, text files, configs |
-d |
Directory | /home, /tmp, /var |
-L |
Symbolic link | /usr/bin/python -> python3 |
-b |
Block device | /dev/sda, /dev/nvme0n1 |
-c |
Character device | /dev/null, /dev/tty |
-p |
Named pipe (FIFO) | Inter-process communication |
-S |
Socket | /var/run/docker.sock |
-f operator follows symbolic links. To check if something is a symlink regardless of what it points to, use -L. To check if a symlink points to a valid target, combine them: [[ -L "$link" && -e "$link" ]].
File Comparison Operators
These operators compare files by modification time and identity (same inode). Perfect for backup scripts and synchronization!
Click Run to execute your code
| Operator | Description | Use Case |
|---|---|---|
file1 -nt file2 |
file1 is newer than file2 | Backup/sync decisions |
file1 -ot file2 |
file1 is older than file2 | Cache invalidation |
file1 -ef file2 |
Same file (same device & inode) | Hard link detection |
-nt and -ot operators return false if either file doesn't exist. Always check existence first with -e if you're not sure the files exist!
String Length Tests
While not strictly file operators, -z and -n are often used alongside file tests to check variable values.
# -z: String is zero length (empty)
filename=""
if [[ -z "$filename" ]]; then
echo "No filename provided"
fi
# -n: String is non-zero length (not empty)
filename="script.sh"
if [[ -n "$filename" ]]; then
echo "Filename: $filename"
fi
# Common pattern: validate before using
if [[ -n "$1" && -f "$1" ]]; then
echo "Processing file: $1"
else
echo "Please provide a valid filename"
fi
-z= Zero length (empty string)-n= Non-zero length (has content)
Common Mistakes
1. Forgetting to quote the filename
# Wrong - breaks on spaces or special chars
file="my file.txt"
[[ -f $file ]] # Error!
# Correct - always quote
[[ -f "$file" ]] # Works correctly
2. Confusing -e and -f
# -e returns true for directories too
[[ -e "/tmp" ]] # True (exists)
[[ -f "/tmp" ]] # False (not a regular file)
# Use -f when you specifically need a regular file
# Use -e when any file type is acceptable
3. Not checking existence before operations
# Wrong - may fail silently or with error
cat "$config_file"
# Correct - check first
if [[ -f "$config_file" ]]; then
cat "$config_file"
else
echo "Config file not found!"
exit 1
fi
4. Using -nt/-ot without checking existence
# Risky - returns false if file doesn't exist
[[ "$backup" -nt "$source" ]]
# Safer - verify both exist
if [[ -f "$backup" && -f "$source" ]]; then
[[ "$backup" -nt "$source" ]] && echo "Backup is current"
fi
Exercise: File Validation Script
Task: Create a comprehensive file validation script!
Requirements:
- Check if a file path was provided (not empty)
- Verify the file exists
- Check if it's a regular file (not a directory)
- Verify it's readable
- Check if it's not empty (size > 0)
- Report all findings
Show Solution
#!/bin/bash
# File Validation Script
file="$1" # Get filename from argument
echo "=== File Validation Report ==="
echo ""
# Check 1: Path provided?
if [[ -z "$file" ]]; then
echo "ERROR: No file path provided"
echo "Usage: $0 "
exit 1
fi
echo "File: $file"
echo ""
# Check 2: Exists?
echo -n "Exists: "
if [[ -e "$file" ]]; then
echo "YES"
else
echo "NO - File not found!"
exit 1
fi
# Check 3: Regular file?
echo -n "Regular file: "
if [[ -f "$file" ]]; then
echo "YES"
else
echo "NO (might be directory or special file)"
fi
# Check 4: Readable?
echo -n "Readable: "
if [[ -r "$file" ]]; then
echo "YES"
else
echo "NO - Permission denied"
fi
# Check 5: Non-empty?
echo -n "Has content: "
if [[ -s "$file" ]]; then
echo "YES (size > 0)"
else
echo "NO (empty file)"
fi
# Check 6: Writable?
echo -n "Writable: "
if [[ -w "$file" ]]; then
echo "YES"
else
echo "NO"
fi
echo ""
echo "Validation complete!"
Summary
- Existence:
-e(any),-f(file),-d(directory),-L(symlink) - Permissions:
-r(readable),-w(writable),-x(executable) - Size:
-s(size > 0) - Types:
-b(block),-c(char),-p(pipe),-S(socket) - Comparison:
-nt(newer),-ot(older),-ef(same file) - Strings:
-z(empty),-n(not empty) - Always Quote: Use
[[ -f "$file" ]]to handle spaces and special characters
What's Next?
Congratulations! You've completed the Operators & Arithmetic module. You now know how to perform calculations, compare values, combine conditions, and test files. Next, we'll dive into Control Flow with if statements, case statements, and loops to put all these operators to work!
Enjoying these tutorials?