Bash Directory Existence Check Fails with Tilde (~) Expansion: Debugging and Solutions


3 views

When checking directory existence in bash scripts, many developers encounter unexpected behavior with the tilde (~) character. The initial approach:

if [ ! -d "~/Desktop" ]; then
   echo "DOES NOT EXIST"
   exit 1;
fi

fails because bash doesn't perform tilde expansion inside quotes or when the tilde is part of a larger string. The shell literally looks for a directory named "~" (tilde character) in the current directory.

Tilde expansion occurs before variable expansion and command substitution in bash's parsing order. For proper expansion, the tilde must:

  • Be unquoted
  • Appear at the beginning of a word
  • Not be followed immediately by alphanumeric characters or slash

For hardcoded paths:

if [ ! -d ~/Desktop ]; then
   echo "Directory doesn't exist"
   exit 1;
fi

For user-provided input in variables:

read -p "Provide destination directory: " DESTINATION

# Expand tilde if present at start
if [[ "$DESTINATION" = ~* ]]; then
    eval "expanded_path=$DESTINATION"
else
    expanded_path="$DESTINATION"
fi

if [ ! -d "$expanded_path" ]; then
    echo "'$expanded_path' does not exist" >&2
    exit 1;
fi

Using bash's built-in parameter expansion:

dir="${DESTINATION/#\~/$HOME}"
if [ ! -d "$dir" ]; then
    # Handle missing directory
fi

Using Python as a more robust alternative:

#!/usr/bin/env python3
import os
import sys

path = os.path.expanduser(input("Enter path: "))
if not os.path.isdir(path):
    sys.exit(f"Error: {path} doesn't exist")

When using eval for path expansion (as shown earlier), always:

  • Validate user input first
  • Consider using more secure alternatives
  • Restrict script permissions appropriately

For scripts that need to run across different Unix-like systems:

#!/bin/bash
DEFAULT_DIR="$HOME/Desktop"

if [ ! -d "${DESTINATION:-$DEFAULT_DIR}" ]; then
    echo "Directory not found" >&2
    exit 1
fi

When writing bash scripts, many developers encounter this frustrating scenario: a directory check with ~ mysteriously fails even when the directory exists. Here's why your check might be failing:

if [ ! -d "~/Desktop" ]; then
   echo "DOES NOT EXIST"
   exit 1;
fi

This happens because the tilde character doesn't expand inside quotes in bash. The shell sees the literal string "~/Desktop" rather than expanding it to /Users/yourusername/Desktop.

Here are several robust solutions for directory existence checks:

# Solution 1: Use $HOME variable
if [ ! -d "$HOME/Desktop" ]; then
   echo "Directory doesn't exist"
fi

# Solution 2: Let bash expand tilde before quoting
dir=~/Desktop
if [ ! -d "$dir" ]; then
   echo "Directory doesn't exist"
fi

# Solution 3: For user input, use eval carefully (security warning)
read -p "Enter directory: " DESTINATION
expanded_dir=$(eval echo "$DESTINATION")
if [ ! -d "$expanded_dir" ]; then
   echo "Directory doesn't exist"
fi

For scripts accepting user input that might contain tildes, consider this more secure approach:

read -p "Provide the destination directory: " DESTINATION

# Expand tilde while preventing command injection
expanded_dir=${DESTINATION/#\~/$HOME}

if [ ! -d "$expanded_dir" ]; then
    echo "'$DESTINATION' does not exist." >&2;
    exit 1;
fi

In bash, the double bracket syntax handles tilde expansion differently:

if [[ ! -d ~/Desktop ]]; then
   echo "DOES NOT EXIST"
fi

This works because [[ ]] performs tilde expansion before the test. However, for maximum portability across shells, the $HOME solution is preferable.

When debugging directory checks:

  • Add echo "Testing: $dir" before your check
  • Use ls -ld ~/Desktop to verify the path
  • Check permissions with stat ~/Desktop