Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

This is probably very simple, but I can't figure it out. I have a directory structure like this (dir2 is inside dir1):

        --- file1
        --- file2

What is the best way to 'flatten' this director structure in such a way to get file1 and file2 in dir1 not dir2.

share|improve this question

You can do this with GNU find and GNU mv:

find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +

Basically, the way that works if that find goes through the entire directory tree and for each file (-type f) that is not in the top-level directory (-mindepth 2), it runs a mv to move it to the directory you want (-exec mv … +). The -t argument to mv lets you specify the destination directory first, which is needed because the + form of -exec puts all the source locations at the end of the command.

As Stephane Chazelas points out, the above only works with GNU tools (which are standard on Linux, but not most other systems). The following is somewhat slower (because it invokes mv multiple times) but much more universal:

find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'
share|improve this answer
Edited to use -exec + so that it doesn't execute a large number of processes of mv – Random832 Oct 24 '12 at 19:32
@Random832 And going to revert again, because + doesn't work. mv needs the destination as the final argument, but + would have the sources as the final argument. Find wont even accept the syntax you changed it to (find: missing argument to `-exec') – derobert Oct 24 '12 at 19:34
@Random832 but I suppose mv has a -t we can use, so I'll change it to that. – derobert Oct 24 '12 at 19:36
@Random832 See How can I use two bash commands in -exec of find command? – Gilles Oct 24 '12 at 22:10
@Dom find prints hidden (dot) files by default. The depth is relative to the directory you pass to find. – derobert Jul 15 '13 at 15:32

In zsh:

mv dir1/*/**/*(.D) dir1

**/ traverses subdirectories recursively. The glob qualifier . matches regular files only, and D ensures that dot files are included (by default, files whose name starts with a . are excluded from wildcard matches). To clean up now-empty directories afterwards, run rmdir dir1/**/*(/Dod)/ restricts to directories, and od orders the matches depth first so as to remove dir1/dir2/dir3 before dir1/dir2.

If the total length of the file names is very large, you may run into a limitation on the command line length. Zsh has builtins for mv and rmdir which are not affected by this limitation: run zmodload zsh/files to enable them.

With only POSIX tools:

find dir1 -type f -exec mv {} dir1 \;
find dir1 -depth -exec rmdir {} \;

or (faster because it doesn't have to run a separate process for each file)

find dir1 -type f -exec sh -c 'mv "$@" dir1' _ {} +
find dir1 -depth -exec rmdir {} +
share|improve this answer

Try doing this :

cp /dir1/dir2/file{1,2} /another/place

or for each files matching file[0-9]* in the subdir :

cp /dir1/dir2/file[0-9]* /another/place

See http://mywiki.wooledge.org/glob

share|improve this answer
I should have indicated this, but I have to many files to use {} in my real problem. – turtle Oct 24 '12 at 19:30
See my second solution – Gilles Quenot Oct 24 '12 at 19:31
Bingo. Thanks for the help. This is definitely the best solution. – turtle Oct 24 '12 at 19:34

I wrote two functions you can use together that do just that, you can limit the directory level by adding a -maxdepth $VAL parameter.

# This scripts flattens the file directory
# Run this script with a folder as parameter:
# $ path/to/script path/to/folder


    local DIR="$1"
    for dir in "$DIR"/*/
        [ -d "${dir}" ] || continue # if not a directory, skip
        if [ "$(ls -A "$dir")" ]; then
            rmEmptyDirs "$dir"
            rmdir "$dir"

    local DIR="$1"
    find "$DIR" -mindepth 2 -type f -exec mv -i '{}' "$DIR" ';'

flattenDir "$1"
rmEmptyDirs "$1"
echo "Done"
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.