0

I have a directory tree containing photos & videos, where I've "tagged" some information via the path. What I'd like to do is move all folders of a specific name into a separate folder, but reproduce the paths leading up to each instance of folders of that name. For example, starting with:

/Media
.../Pics
....../Travel
........./Round-The-World1
........./Round-The-World2
............./Japan
............./Europe
................/Italy
................/Spain
....../Home
.../Vids
....../Travel
........./Round-The-World1
........./Round-The-World2
............./Japan
............./Europe
................/Italy
................/Spain
....../Home

Let's say I wanted to move the folders with the exact name "Europe" (i.e. not with "Europe" as a substring - only folders that are named "Europe," and their children). The desired result:

/Media
.../Pics
....../Travel
........./Round-The-World1
........./Round-The-World2
............./Japan
....../Home
.../Vids
....../Travel
........./Round-The-World1
........./Round-The-World2
............./Japan
....../Home

/New .../Pics ....../Travel ........./Round-The-World2 ............./Europe ................/Italy ................/Spain .../Vids ....../Travel ........./Round-The-World2 ............./Europe ................/Italy ................/Spain

It's somewhat similar to Extract files with specific file extension and keep directory structure?, but I'm quite a beginner with the Linux cli, and don't want to risk screwing up my organization - so any help would be appreciated :)

J23
  • 437
  • 3
  • 7
  • 16

1 Answers1

1

One thing you need to take into consideration before looking at the following answer is that you will effectively be recreating the whole path all the way from /

This will be a multi-step process, because you are looking to recreate the parent folder structure, and not precisely copy it and its contents. The structure is as follows

home/
├── fol_a
│   ├── fol_ABC
│   │   └── My Folder
│   │       └── file 2
│   └── fol_XYZ
│       └── My Folder
│           ├── file1
│           └── fol_extra
│               └── file 3
└── my test

11 directories, 3 files

in other words the full path of file1 is /home/fol_a/fol_XYZ/My Folder/file1. Our goal is to move folders My Folder and their contents, to the my test folder such that their paths become /home/my test/home/fol_a/fol_XYZ/My Folder/* and /home/my test/home/fol_a/fol_ABC/My Folder/*

1. Rebuild directory structure and copy appropriate folders and their contents

Running from the parent folder of the tree branch where folders of the same name are located (in our example it would be fol_a , but in your example it would be /Media ) , you can do the following:

NOTE: export these variables , as DEST and SOURCE will be necessary for feeding variables into bash -c of find

DEST=target path SOURCE=name of folder to copy, with its full path recreated BRANCH=the common branch of the same-named folders you wish to copy

export DEST="/home/my test"
export SOURCE="My Folder"
export BRANCH="/home/fol_a"
find "$BRANCH" -type d -name "$SOURCE" -exec bash -c 'mkdir -p "$DEST""$(realpath "{}")" ; cp -r "{}"/* "$DEST""$(realpath "{}")"' \;

NOTE: If you get a message like cp: cannot stat './[FOLDERNAME]/*': No such file or directory **it is not an error, it just means that ** cp -r [FOLDERNAME]/* command happened to execute on a folder that was empty


  • Check the recreated folder tree and contents in your destination folder

To quickly check the folder structure and the files to see if the copying went correctly you can run

tree "$DEST"/..

At this stage(before removing) your overall folder tree will look like so:

home/
├── fol_a
│   ├── fol_ABC
│   │   └── My Folder
│   │       └── file 2
│   └── fol_XYZ
│       └── My Folder
│           ├── file1
│           └── fol_extra
│               └── file 3
└── my test
    └── home
        └── fol_a
            ├── fol_ABC
            │   └── My Folder
            │       └── file 2
            └── fol_XYZ
                └── My Folder
                    ├── file1
                    └── fol_extra
                        └── file 3

14 directories, 6 files


2. Remove the folder tree from the old common branch

Finally (and after double checking all the files are actually within appropriate folders etc.) if everything looks correct , and you are looking to truly move fol_d and its contents (and not just copy) you can finish it off with the following

From the same previously declared common branch (in the above example it was /home/fol_A)

find "$BRANCH" -type d -name "$SOURCE" -exec rm -r "{}" \;

NOTE: You might get messages like find [$SOURCE] :No such file or directory but this will be more or less normal when deleting contents and directory using find and -exec directive.

One last check is to run tree and the final output becomes:

home/
├── fol_a
│   ├── fol_ABC
│   └── fol_XYZ
└── my test
    └── home
        └── fol_a
            ├── fol_ABC
            │   └── My Folder
            │       └── file 2
            └── fol_XYZ
                └── My Folder
                    ├── file1
                    └── fol_extra
                        └── file 3

11 directories, 3 files

NetIceCat
  • 2,294
  • If I'm understanding this correctly, this just handles for one specific fol_d? (i.e. you said "Starting from parent folder of fol_d"). I'm looking to do this for "each instance of folders of that name." i.e. there may be 50 or 100 folders named "fol_d," and all of them should be moved to outside of the source tree. – J23 Oct 01 '20 at 04:51
  • Sorry didn't notice that last part, updating my answer accordingly right away – NetIceCat Oct 01 '20 at 04:54
  • Still says "Starting from parent folder of fol_d (folder fol_c in the example)" tho - should it now be "starting from any descendant folder of fol_d"? Since again, if it's just the direct parent of that one folder, it wouldn't capture all the other fol_d's on the same level. – J23 Oct 01 '20 at 05:16
  • Yes, anywhere from upper common branch will work, in your example it would be /Media folder – NetIceCat Oct 01 '20 at 06:14
  • Trying that exact command (copy-pasted from here), I get: bash: -c: line 0: syntax error near unexpected token `(' – J23 Oct 01 '20 at 18:19
  • have you declared both SOURCE and DEST? – NetIceCat Oct 01 '20 at 20:40
  • 1
    Yup, I did all 3 commands, including specifying a DEST that I pre-created. Could it have something to do with spaces appearing in paths? Neither SOURCE or DEST have spaces, but some of the photo folders do. – J23 Oct 02 '20 at 06:18
  • yes! GOOD CATCH. all you need to do is surround the DEST and SOURCE with quotations marks.Ill update the answer. – NetIceCat Oct 02 '20 at 22:23
  • Ok, I rewrote a good majority of my answer, taking into account both multiple folders in the subtree, as well as spaces in folder names. In the find command I also replaced directly written paths with variables you can export, such that this can be easily converted into a script with 3 arguments: $DEST $SOURCE and $BRANCH . I would highly advise replacing spaces in folder names, with underscores if possible (I know its a hassle when jumping between Linux and Windows that likes such spaces ) – NetIceCat Oct 03 '20 at 01:47
  • Final note: the above also allows for multiple repetitions of the same $SOURCE folder name in the same subtree. For example ./My Folder/My Folder/My Folder/* without errors or crazy loops. – NetIceCat Oct 03 '20 at 01:57
  • Excellent, thanks!! Accepted :) – J23 Oct 04 '20 at 05:05