1

I have a large n-level directory structured as follows:

root
  |
   subdir1
      |
       sub_subdir1
           |
            ....(n-2 levels).....
                                 |
                                  file1
  |
   subdir2
      |
       sub_subdir2
           |
            ....(n-2 levels).....
                                 |
                                  file2

I want to flatten the directory so that all level 1 subdirs contain files. I also want to remove the level 2 to (n-1) sub_subdirs as they contain no files. Please note that the subdirs all have different names.

Desired Result

root
  |
   subdir1
      |
       file1
  |
   subdir2
      |
       file2

I have found a lot of posts explaining methods to flatten directories but none that explains how to do this in a controlled manner, i.e.,

  • by specifying the levels to be flattened
  • or doing it recursively for all sub_directories in a root directory
  • Did you find any solutions to flatten a directory structure, moving all the files to the top level and keeping no subdirectories? If so, just apply that solution in each top-level subdirectory. – Scott Jul 24 '19 at 18:04
  • True. This can be done. However, I am just looking for a more functional solution. Thanks tho ! – sheth7 Jul 25 '19 at 16:13
2

Using zsh:

cd /root
for subdir in subdir*
do
  mv "$subdir"/**/*(.) "$subdir"
  rm -r "$subdir"/*(/)
done

This:

  1. changes to the "/root" directory (from your example)
  2. loops over every subdirectory named subdir* (again from your example: matching subdir1 and subdir1)
  3. moves the (expected one, but would match all) matching file(s) under that subdirectory into that subdirectory. This uses zsh's ** recursive globbing feature, limited then by the glob qualifier *(.) which says: any entry in this directory that's a plain file
  4. after moving the file, recursively remove every subdirectory under that subdirectory; this again uses a zsh glob qualifier *(/) which says to match entries that are directories.
| improve this answer | |
  • Thanks. I am sorry, my question does not clarify this. The subdirs in my actual structure have names and the names are all different. So name matching won't work. How can I work around that? Thanks! – sheth7 Jul 25 '19 at 16:11
  • Then, from the root directory, simply select everything: for subdir in */ – Jeff Schaller Jul 25 '19 at 17:41
  • why do I have error for the line of mv: syntax error near unexpected token '(' – Brian Hong Apr 13 at 1:42
  • @BrianHong are you using zsh? – Jeff Schaller Apr 13 at 2:19
1

In bash, do the following:

shopt -s dotglob
for d in root/*/
do
        find "$d" -type f -exec mv -i -t "$d" {} +
        find "$d" -mindepth 1 -type d -delete
done

This looks at everything in “root”, including dot-files.  If you don’t need to look at dot-files, leave out the shopt -s dotglob.  Warning: do not add .* or root/.* to the for command.

The / at the end of the glob (root/*/), as suggested by Jeff, gets us only the subdirectories, so we don’t need to test each one (e.g., with if).

For every subdirectory of root, find all files in and under that subdirectory and move them into the subdirectory.  Use -i to prevent overwriting files (e.g., if you have multiple files called file1 distributed under subdir1).  -i means “interactive”, and it will ask you whether it should overwrite an existing file.  Be aware that it might refuse to do so, even if you say “yes”.  If your version of mv doesn’t support the -t option, replace the line with the mv command with

        find "$d" -type f -exec mv -i {} "$d" ';'

Then delete all the subdirectories under the top-level subdir.  Note that this (find … -delete) will fail if any files didn’t get moved.

This is partially inspired by derobert’s answer to Flattening a nested directory.

| improve this answer | |
  • Thanks! Since I am on a mac, the target-directory flag -t is not allowed. Would you know a workaround? – sheth7 Jul 26 '19 at 14:32
  • (1) I updated my answer.  (Does the system notify you of that?)  (2) If you wanted a zsh answer, it would have been nice if you had said so in the question. – Scott Jul 26 '19 at 18:47
  • Thanks. No, I do not have a requirement as such. Both answers are good. Wish I could green check both. – sheth7 Jul 26 '19 at 22:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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