funwithlinux blog

Shell Variable Issue: Why mkdir Fails with ~/Path in Scripts (and How to Fix It)

If you’ve ever written a shell script and encountered an error like mkdir: cannot create directory ‘~/myfolder’: No such file or directory when trying to create a directory with a ~/path syntax, you’re not alone. This frustrating issue is a common pitfall in shell scripting, rooted in how Unix-like shells handle tilde expansion and variable expansion.

In this blog, we’ll demystify why mkdir (or any command) fails when using ~/path inside a script, break down the underlying shell mechanics, and provide actionable solutions to fix it. By the end, you’ll understand how to reliably handle home directory paths in scripts.

2026-01

Table of Contents#

  1. Understanding the Problem: Why ~/Path Fails in Scripts
  2. The Root Cause: Tilde Expansion in Shells
  3. Diagnosing the Issue: Examples and Error Messages
  4. How to Fix It: Solutions for Tilde Paths in Scripts
  5. Best Practices for Path Handling in Shell Scripts
  6. Conclusion
  7. References

Understanding the Problem: Why ~/Path Fails in Scripts#

Let’s start with a concrete example. Suppose you write a simple script (create_dir.sh) to create a directory in your home folder:

#!/bin/bash
# create_dir.sh
 
DIR="~/my_new_folder"  # Path with tilde (~)
mkdir "$DIR"           # Attempt to create the directory

When you run this script with bash create_dir.sh, you get an error:

mkdir: cannot create directory ‘~/my_new_folder’: No such file or directory

But if you run mkdir ~/my_new_folder directly in your interactive shell (e.g., terminal), it works! So why does the same path fail in a script?

The Root Cause: Tilde Expansion in Shells#

The key to solving this lies in understanding tilde expansion and the order of operations in shell expansion.

What is Tilde Expansion?#

In Unix shells (like Bash, Zsh, or Dash), the tilde (~) is a special character that expands to the current user’s home directory (e.g., /home/your_username). For example:

  • ~ expands to /home/your_username
  • ~/Documents expands to /home/your_username/Documents
  • ~otheruser expands to /home/otheruser (if otheruser exists)

The Order of Shell Expansions#

Shells process commands in a specific order of expansions. From the Bash manual, the order is:

  1. Brace Expansion ({a,b}a b)
  2. Tilde Expansion (~/home/user)
  3. Parameter and Variable Expansion ($VAR → value of VAR)
  4. Arithmetic Expansion ($((1+1))2)
  5. Command Substitution ($(command) → output of command)
  6. Word Splitting
  7. Filename Expansion (globbing, e.g., *.txt)

Why ~/Path Fails in Variables#

Notice that tilde expansion happens before variable expansion. When you store ~/my_new_folder in a variable (e.g., DIR="~/my_new_folder"), the shell treats the ~ as a literal character during variable assignment because:

  • Tilde expansion occurs early (step 2), but the variable is defined as a string ("~/my_new_folder"), so no expansion happens here.
  • Later, when the variable is expanded (step 3, e.g., mkdir "$DIR"), tilde expansion has already finished. The shell sees ~/my_new_folder as a literal path with a ~ character, not a home directory shortcut.

Diagnosing the Issue: Examples and Error Messages#

To confirm the problem, let’s compare behavior in an interactive shell vs. a script.

Interactive Shell: Quoting vs. Not Quoting#

In an interactive shell, if you avoid quotes when assigning the variable, tilde expansion does occur during assignment (because tilde expansion happens before variable assignment in this case):

# Interactive shell example
DIR=~/my_new_folder  # No quotes: tilde expands DURING assignment
echo "$DIR"          # Output: /home/your_username/my_new_folder (expanded!)
mkdir "$DIR"         # Works!

But if you use quotes when assigning the variable (as in the script), tilde expansion does NOT occur:

# Interactive shell example with quotes
DIR="~/my_new_folder"  # Quotes: tilde is treated as a literal
echo "$DIR"            # Output: ~/my_new_folder (NOT expanded)
mkdir "$DIR"           # Fails: "No such file or directory"

Script Behavior#

In scripts, even without quotes during assignment, the behavior is consistent with the expansion order. However, scripts often use quotes around variables (a best practice to avoid word splitting), which preserves the literal ~ if the variable wasn’t expanded during assignment.

The Error Message Explained#

The error mkdir: cannot create directory ‘~/my_new_folder’ occurs because the shell tries to create a directory named ~ (a literal tilde) in the current working directory, then a subdirectory my_new_folder inside it. Since the ~ directory doesn’t exist, the command fails.

How to Fix It: Solutions for Tilde Paths in Scripts#

Now that we understand the root cause, let’s explore reliable solutions to use ~/path-like paths in scripts.

Solution 1: Use $HOME Instead of ~#

The simplest fix is to replace ~ with the $HOME environment variable, which explicitly expands to your home directory.

#!/bin/bash
# create_dir_fixed.sh
 
DIR="$HOME/my_new_folder"  # Use $HOME instead of ~
mkdir "$DIR"               # Now works!

Why it works: $HOME is a variable that expands to your home directory (e.g., /home/your_username). Since variable expansion (step 3) replaces $HOME with its value, the path becomes /home/your_username/my_new_folder, which mkdir understands.

Solution 2: Expand Tilde During Variable Assignment#

If you prefer using ~, expand it during variable assignment by avoiding quotes around the path. This ensures tilde expansion (step 2) runs before the variable is stored.

#!/bin/bash
# create_dir_fixed.sh
 
DIR=~/my_new_folder  # No quotes: tilde expands during assignment
mkdir "$DIR"         # $DIR is already expanded (e.g., /home/user/my_new_folder)

Why it works: Without quotes, the shell processes tilde expansion (step 2) before assigning the value to DIR. Thus, DIR stores the fully expanded path (e.g., /home/your_username/my_new_folder), not the literal ~.

Solution 3: Use eval to Force Tilde Expansion (Caution!)#

If you must keep the tilde in a quoted variable (e.g., dynamic paths), use eval to re-run tilde expansion. Note: eval executes strings as shell commands, so use this only with trusted input (avoid untrusted user input!).

#!/bin/bash
# create_dir_fixed.sh
 
DIR="~/my_new_folder"  # Tilde in quoted variable (literal ~)
eval "mkdir $DIR"      # eval forces tilde expansion

Why it works: eval re-parses the command string, triggering tilde expansion on ~/my_new_folder before executing mkdir.

Risks: If DIR contains spaces, special characters, or malicious input (e.g., ~/malicious; rm -rf /), eval will execute those commands. Avoid this unless you fully control the DIR value.

Solution 4: Expand ~otheruser for Other Users#

If the path belongs to another user (e.g., ~john/documents), use Solution 2 (unquoted assignment) to expand the tilde during variable creation:

#!/bin/bash
# create_johns_dir.sh
 
USER_DIR=~john/documents  # Unquoted: expands to /home/john/documents
mkdir "$USER_DIR"         # Works if user "john" exists

Best Practices for Path Handling in Scripts#

To avoid tilde-related issues (and other path pitfalls) in scripts, follow these best practices:

  1. Prefer $HOME over ~ in variables: It’s explicit, readable, and avoids expansion-order confusion.
  2. Quote variables when using them (e.g., mkdir "$DIR"): This prevents word splitting if paths contain spaces (e.g., $HOME/My Documents).
  3. Expand tildes during assignment if using ~: Use unquoted assignment (DIR=~/path) to ensure tilde expansion happens early.
  4. Avoid eval with untrusted input: eval is powerful but risky. Use it only if you fully control the path.
  5. Test paths with echo: Before running mkdir or cd, echo the path to verify expansion:
    DIR="$HOME/my_new_folder"
    echo "Creating directory: $DIR"  # Verify the path is correct!
    mkdir "$DIR"

Conclusion#

The "mkdir fails with ~/path in scripts" issue is caused by the shell’s expansion order: tilde expansion happens before variable expansion, so ~ in a variable is treated as a literal. To fix it:

  • Use $HOME (e.g., DIR="$HOME/path"),
  • Expand ~ during variable assignment (e.g., DIR=~/path), or
  • Use eval (with caution) for dynamic paths.

By following these solutions and best practices, you’ll reliably handle home directory paths in shell scripts.

References#