Here are 10 common shell scripting "gotchas" that even experienced developers can fall into:

  1. 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
    
    
  2. 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"
    
    
  3. 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
    
    
  4. 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
    
    
  5. 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
    
    
  6. Not handling empty input - Consider what happens when input is empty:

    cat $file | grep "pattern"  # If $file doesn't exist, grep reads from stdin
    
    
  7. 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
    
    
  8. Improper variable scoping - Variables modified in subshells don't affect parent:

    var=1
    (var=2)
    echo $var  # Prints 1, not 2
    
    
  9. 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
    
    
  10. 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