Recursive File Movement in Unix/Linux: Moving All Files (Including Hidden) from Subfolders to Parent Directory


3 views

Every Unix/Linux administrator eventually faces this scenario: You have a directory structure where files are nested several levels deep, and you need to bring them all up to a parent directory while preserving the recursive structure. The challenge intensifies when dealing with hidden files (those starting with .) which don't get caught by standard wildcards.

The fundamental issue stems from how shell globbing works:

mv subfolder/* .        # Misses hidden files
mv subfolder/.* .       # Would move '.' and '..' (dangerous!)

Here's the safest, most comprehensive approach:

find /path/to/foo/bar -mindepth 1 -maxdepth 1 -exec mv -t /path/to/foo -- {} +

Breaking this down:

  • -mindepth 1 excludes the directory itself
  • -maxdepth 1 prevents recursion into sub-subfolders
  • -t specifies target directory first (safer with multiple files)
  • -- prevents interpretation of filenames starting with '-' as options

Using dotglob

For bash users, you can temporarily enable dotglob:

shopt -s dotglob
mv foo/bar/* foo/
shopt -u dotglob

Rsync Method

For complex moves or remote filesystems:

rsync -a --remove-source-files foo/bar/ foo/ && rm -rf foo/bar

When files might collide during the move:

find foo/bar -mindepth 1 -maxdepth 1 -exec mv -it foo -- {} +

The -i flag makes mv interactive, prompting before overwrites.

To move files up just one level but maintain deeper structures:

find foo/bar -type f -exec mv --backup=numbered {} foo \;

This creates numbered backups of any conflicting files.

For very large directories, this parallel approach can be faster:

find foo/bar -mindepth 1 -maxdepth 1 -print0 | xargs -0 -P 4 -I {} mv {} foo/

The -P 4 runs 4 parallel mv operations.

Always verify with echo first:

find foo/bar -mindepth 1 -maxdepth 1 -exec echo mv -ivt foo -- {} +

This dry-run shows what would happen without actually moving files.


When working with Unix-like systems, a common task is consolidating files from a subdirectory into its parent directory. The challenge intensifies when dealing with hidden files (dotfiles) and nested directories. Traditional wildcards fail to capture these hidden items, requiring a more nuanced approach.

The standard * wildcard in shell commands doesn't match files beginning with a dot (.). While .* matches only dotfiles, we need a solution that captures both regular and hidden files.

Here's the most reliable method using find and xargs:

find bar/ -mindepth 1 -maxdepth 1 -print0 | xargs -0 -I {} mv {} foo/

Breakdown:

  • -mindepth 1: Excludes the bar directory itself
  • -maxdepth 1: Prevents recursion into subdirectories
  • -print0: Handles filenames with spaces or special characters
  • xargs -0: Safely processes the null-delimited output

Using mv with Extended Globbing

For bash shells with extglob enabled:

shopt -s dotglob
mv bar/{*,.*} foo/

For Recursive Movement

When you need to flatten the entire directory structure:

find bar/ -type f -exec mv {} foo/ \;

To prevent overwrites, use the -n flag with mv:

find bar/ -mindepth 1 -maxdepth 1 -exec mv -n {} foo/ \;

For directories with thousands of files, the find+xargs method is significantly faster than -exec, as it minimizes process creation overhead.

If you encounter permission errors, consider running with sudo or adjusting ownership first:

sudo find bar/ -mindepth 1 -maxdepth 1 -exec chown $USER {} \;
find bar/ -mindepth 1 -maxdepth 1 -exec mv {} foo/ \;