Bash One-Liner: Move All Files to a Subdirectory While Excluding the Target Directory Itself


2 views

When organizing messy directories, we often want to consolidate files into a subdirectory. The naive approach mv * ./new_directory fails because it attempts to move the target directory into itself, creating an infinite loop.

Here are several production-tested approaches that handle edge cases like hidden files, spaces in filenames, and pre-existing directories:

# Method 1: Negative pattern matching
mkdir -p new_directory && mv !(new_directory) new_directory/ 2>/dev/null

# Method 2: find + xargs (most reliable)
mkdir -p new_directory && find . -maxdepth 1 -mindepth 1 -not -name new_directory -exec mv {} new_directory/ \;

# Method 3: Extended glob (requires shopt -s extglob)
shopt -s extglob
mkdir -p new_directory && mv !(new_directory|.|..) new_directory/

For complex scenarios with thousands of files or permission issues:

# Using rsync for atomic operations
mkdir -p new_directory && \
rsync -a --remove-source-files --exclude='new_directory' ./ ./new_directory/ && \
find . -maxdepth 1 -mindepth 1 -empty -delete

# Parallel processing for massive directories
mkdir -p new_directory && \
find . -maxdepth 1 -mindepth 1 -not -name new_directory -print0 | \
xargs -0 -P 8 -I {} mv {} new_directory/

Always include safety checks:

if [ -d "new_directory" ]; then
    echo "Error: Target directory exists" >&2
    exit 1
fi
mkdir new_directory || exit 1
mv -- * ./new_directory/ 2>/dev/null

When trying to organize files by moving them into a new subdirectory, a naive approach like mv * ./new_directory fails because it attempts to move the target directory into itself. This creates a conflict and results in an error:

$ mkdir new_directory
$ mv * ./new_directory
mv: cannot move 'new_directory' to a subdirectory of itself

Here are three reliable methods to move all files (including hidden files) and subdirectories while excluding the target directory:

Method 1: Using extended globbing

shopt -s extglob
mkdir new_directory
mv !(new_directory) new_directory/

Method 2: With find (handles edge cases)

mkdir new_directory
find . -mindepth 1 -maxdepth 1 -not -name new_directory -exec mv {} new_directory/ \;

Method 3: Simple exclusion pattern

mkdir new_directory
mv !(new_directory|.|..) new_directory/ 2>/dev/null

For production scripts, consider these enhancements:

  • Add error handling for existing directories
  • Include dotfiles (hidden files) with shopt -s dotglob
  • Handle spaces in filenames properly
#!/bin/bash
TARGET_DIR="organized_files"

if [ -d "$TARGET_DIR" ]; then
    echo "Error: $TARGET_DIR already exists" >&2
    exit 1
fi

mkdir "$TARGET_DIR" || exit 1
shopt -s dotglob extglob
mv !("$TARGET_DIR"|.|..) "$TARGET_DIR/" 2>/dev/null