Here are 10 common shell scripting "gotchas" that even experienced developers can fall into:
Silent overwriting with mv - The default behavior overwrites destination files without warning:
mv source.txt destination.txt # Overwrites destination.txt silently
mv -i source.txt destination.txt # Interactive mode asks first
mv -n source.txt destination.txt # No-clobber prevents overwriting
Spaces in filenames - Unquoted variables with spaces will be interpreted as multiple arguments:
file="my document.txt"
rm $file # Wrong: tries to delete "my" and "document.txt"
rm "$file" # Correct: deletes "my document.txt"
Forgetting to check command success - Commands quietly fail without halting execution:
mkdir /path/to/dir
cd /path/to/dir # If mkdir failed, you'll be in the wrong directory
rm -rf * # Could be catastrophic in the wrong directory
Using == in test or [ ] - POSIX sh only supports = for string comparison:
if [ "$a" == "$b" ]; then # Works in bash but not in POSIX sh
if [ "$a" = "$b" ]; then # Correct for POSIX sh
Wildcard expansion in rm commands - Empty variables or failed matches can be dangerous:
rm -rf $directory/* # If $directory is empty, expands to "rm -rf /*"
rm -rf "$directory"/ # Safer approach
Not handling empty input - Consider what happens when input is empty:
cat $file | grep "pattern" # If $file doesn't exist, grep reads from stdin
Assuming working directory - Scripts run from unexpected locations:
cd /some/path # Changes directory only for this script
./script.sh # But this script runs in the current directory, not /some/path
Improper variable scoping - Variables modified in subshells don't affect parent:
var=1
(var=2)
echo $var # Prints 1, not 2
Word splitting on unquoted variables - Can lead to unexpected behavior:
files="file1.txt file2.txt"
cp $files /destination/ # Splits into two arguments
cp "$files" /destination/ # Treated as a single filename with a space
Ignoring exit codes in conditionals - Shell treats any non-zero exit code as false, which can mask errors:
if grep "pattern" nonexistent_file.txt; then
echo "Pattern found"
else
echo "Pattern not found" # This runs even if the file doesn't exist
fi
# Better approach - check if file exists first
if [ -f nonexistent_file.txt ] && grep "pattern" nonexistent_file.txt; then
echo "Pattern found"
else
echo "Either file doesn't exist or pattern not found"
fi