Search code examples
bashdirectorysubdirectory

Recursively move folders within subfolders to its parent


I have a master folder (reffered to as level 0) which contains over 50 subfollders, (level 1), and those subfolders consist of lower level subfolders and files (level 2). I need to move every folder and their contents from level 2 to it's level 1 subfolder (or relatively, its parent).

To better visualize this:

Master folder
├ Parent Folder A
│ ├─ Subfolder A
│ │  ├─ File A
│ │  └─ File B
│ ├─ Subfolder B
│ │  ├─ File C
│ │  └─ Folder D
.
. ... more folders ...
.
└─ Parent folder 134
   ├─ Subfolder CS
   │  ├─ File AGF
   │  └─ File ARH
   ├─ File ROQ
   └─ File JGL

I need to move the folders within the parent folders and their files to the master folder

Objective:

Master folder
├─ Subfolder A
│  ├─ File A
│  ├─ File B
│  ├─ File C
│  ├─ File D
│  ├─ File E
│  └─ File F
├─ Subfolder B
│  ├─ File AZA
│  ├─ File AZB
│  ├─ File AZC
│  └─ File AZD
... and so on

The problem here is that there are hundreds of parent and subfolders, and they all have different names that may change. The amount of them may change aswell. I have no idea where to start. I need to do this in a bash script, and while I have found others that have done this, they have all done it within powershell. Thanks for any help anyone can provide.


Solution

  • If I'm reading this right, you want every file under each folder to be moved out of any subfolders up to the first folder under the master.

    A simplistic version of that might look like this:

    $: find # starting test structure
    .
    ./a
    ./a/a.txt
    ./a/e
    ./a/e/ae.txt
    ./a/f
    ./a/f/af.txt
    ./a/g
    ./a/g/ag.txt
    ./b
    ./b/b.txt
    ./b/e
    ./b/e/be.txt
    ./b/f
    ./b/f/bf.txt
    ./b/g
    ./b/g/bg.txt
    ./c
    ./c/c.txt
    ./c/e
    ./c/e/ce.txt
    ./c/f
    ./c/f/cf.txt
    ./c/g
    ./c/g/cg.txt
    $: for f in $PWD/*/;  # for all the subdirectories in the base folder 
    >  do find $f -type f | xargs -Ithis mv this "$f/"; # move all files up to that sub
    >     for d in "$f"/*/; do rm -fr "$d"; done;       # and remove all lower empty subs
    >  done
    $: find # result
    .
    ./a
    ./a/a.txt
    ./a/ae.txt
    ./a/af.txt
    ./a/ag.txt
    ./b
    ./b/b.txt
    ./b/be.txt
    ./b/bf.txt
    ./b/bg.txt
    ./c
    ./c/c.txt
    ./c/ce.txt
    ./c/cf.txt
    ./c/cg.txt
    

    EDIT

    Moving subdirectories makes it a lot easier, I think...

    $: for b in ./*/*/; do cp -r "$b" ./; rm -fr "$b"; done
    $: find
    .
    ./a
    ./a/a.txt
    ./b
    ./b/b.txt
    ./c
    ./c/c.txt
    ./e
    ./e/ae.txt
    ./e/be.txt
    ./e/ce.txt
    ./f
    ./f/af.txt
    ./f/bf.txt
    ./f/cf.txt
    ./g
    ./g/ag.txt
    ./g/bg.txt
    ./g/cg.txt
    

    Note that in my test I had e, f, & g under each of a, b, & c, so the files collected into the single new level 2 directories. If there are any files with the same names in similarly named directories, you'll get whichever processes last.