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