Removing a level in a file structure with Bash when saving files

Thanks in advance for your time and help.

I have a situation where I have a DIR that contains several dozen sub DIRs, where all the sub DIRs contain only one file (for some strange reason). I am trying to move all files two levels in a DIR tree while keeping the file structure in other ways. Here's an example of what I mean:

|--- DIR.Main
|--- |---DIR.Sub-A
|--- |--- |--- File.Sub-A
|--- |---DIR.Sub-B
|--- |--- |--- File.Sub-B
|--- |---DIR.Sub-C
|--- |--- |--- File.Sub-C
|-->Continue for approx. 50 DIR.Subs

      

I don't need the DIR.Sub level in the file structure and would like it to look like this:

|--- DIR.Main
|--- |---File.Sub-A
|--- |---File.Sub-B
|--- |---File.Sub-C

      

Here's the problem of aggravation: the file names are not ordered as logically as in the previous example, but rather are named so randomly:

|--- DIR.Main
|--- |---DIR.Sasjaljesea
|--- |--- |--- File.Sasjalsaejesea.mpg
|--- |---DIR.qwerqwerewqwer
|--- |--- |--- File.qwerqweresezswqwer.mpg
|--- |---DIR.xcbxcvxcvbxcvb
|--- |--- |--- File.xcsfasdbxcvxcvbxcvb.mpg
|-->ETC

      

However, the savings are that, as in the example above, all files are of the same file type (mpg in this case).

I read the documentation for mv and cp in Bash and couldn't find a way to do it in one go without typing all the filenames by hand. I also looked into options with Xrandr and similar commands. The closest I was able to come to a solution:

find /path/to/search -type f -iname "*.mp3" -exec mv {} path/to/music \;

      

Taken from this answer: Moving multiple files to subdirectories (and / or splitting strings by multi-channel divider) [bash]

Where I have obviously fixed -iname for .mpg and DIR to fit my situation.

When I did that, it found all the files and moved them to the correct folder, but it combined all the files into one unreadable .mpg file rather than recursively copying each individual file and maintaining the file structure as it saw fit. I researched using cp recursively in the above command instead of mv, but it didn't seem to do anything even when I was doing SUDO. cp comes with an additional issue that I DON'T WANT to duplicate files, just delete the intermediary folder that contains them.

You probably figured out that I am trying to avoid using the GUI in Linux and go down the Bash path for the obvious reason that it will take a few hours for the window manager, whereas I'm sure there is a way to do it in Bash in seconds. This is a task that usually takes a few seconds in a Linux file manager if you are dealing with less than a dozen folders and files to move, but in the current situation there are dozens of unnecessary DIRs that only wrap one file. Hence, I need a good clean Bash solution.

Thanks again for your help. This is a problem that has disappointed me for several weeks.

Any questions are welcome.

+3


source to share


2 answers


xargs

used with mv

can fix this problem. You must start with the current working directory DIR.Main

and a different directory in order to move everything. It should be different because otherwise you will end up with (empty) original directories associated with your new files and directories. We could work around it, but I don't think we need it.

find -maxdepth 2 -mindepth 2 | xargs -I '{}' mv '{}' <PATH_TO_NEW_LOCATION>/

      



The result find

is a list of all files and directories that are exactly one layer in the directory tree than the current directory. The call then xargs

moves all of these results to the specified location.

I've tested it, but you can test it before using it on your live data, or at least take a copy.

+1


source


It should be as easy as

shopt -s dotglob; mv -iv DIR.Main/*/* DIR.Main/

      



If you pressed ARG_MAX

put it in a loop

shopt -s dotglob; for f in DIR.Main/*/*; do mv -iv "$f" DIR.Main/

      

0


source







All Articles