Peter
Peter

Reputation: 69

Move files to parent directory of current location

I have a lot of folders that have a folder inside them, with files inside. I want to move the 2nd level files into the 1st level and do so without knowing their names.

Simple example:

I'm getting very close but I'm missing something and I'm sure it's simple/stupid. Here's what I'm running:

find . -mindepth 3 -type f -exec sh -c 'mv -i "$1" "${1%/*}"' sh {} \;

Here's what that's getting me:

mv: './1stlevel/2ndlevel/test.rtf' and './1stlevel/2ndlevel/test.rtf' are the same file

Any suggestions?

UPDATE: George, this is great stuff, thank you! I'm learning a lot and taking notes. Using the mv command instead of the more complicated one is brilliant. Far from the first time I've been accused of doing something the hardest way possible!

However, while it works great with 1 set of folders, if I have more, it doesn't work as intended. Here's what I mean:

Before:

new
└── temp
    ├── Folder1
    │   ├── SubFolder1
    │   │   └── SubTest1.txt
    │   └── Test1.txt
    ├── Folder2
    │   ├── SubFolder2
    │   │   └── SubTest2.txt
    │   └── Test2.txt
    └── Folder3
        ├── SubFolder3
        │   └── SubTest3.txt
        └── Test3.txt

After:

new
└── temp
    └── Folder3
        ├── Folder1
        │   ├── SubFolder1
        │   └── Test1.txt
        ├── Folder2
        │   ├── SubFolder2
        │   └── Test2.txt
        ├── SubFolder3
        ├── SubTest1.txt
        ├── SubTest2.txt
        ├── SubTest3.txt
        └── Test3.txt

Desired:

new
└── temp
    ├── Folder1
    │   ├── SubFolder1
    │   ├── SubTest1.txt
    │   └── Test1.txt
    ├── Folder2
    │   ├── SubFolder2
    │   ├── SubTest2.txt
    │   └── Test2.txt
    └── Folder3
        ├── SubFolder3
        ├── SubTest3.txt
        └── Test3.txt

If one wanted to get fancy*:

new
└── temp
    ├── Folder1
    │   ├── SubTest1.txt
    │   └── Test1.txt
    ├── Folder2
    │   ├── SubTest2.txt
    │   └── Test2.txt
    └── Folder3
        ├── SubTest3.txt
        └── Test3.txt

BTW, that took me forever in Notepad++ to draw. What did you use?

Upvotes: 5

Views: 3529

Answers (4)

mrflash818
mrflash818

Reputation: 934

Have your script navigate to each directory where you need the files moved "up," then you can have find find each file in the directory, then move them up one directory:

 $ find . -type f -exec mv {} ../. \;

Upvotes: 0

Your find . -mindepth 3 -type f -exec sh -c 'mv -i "$1" "${1%/*}"' sh {} \; attempt is very close to being right.  A useful technique when debugging complex commands is to insert echo statements to see what is happening.  So, if we say

$ find . -mindepth 3 -type f -exec sh -c 'echo mv -i "$1" "${1%/*}"' sh {} \;
we get

mv -i ./Folder1/SubFolder1/SubTest1.txt ./Folder1/SubFolder1
mv -i ./Folder2/SubFolder2/SubTest2.txt ./Folder2/SubFolder2
mv -i ./Folder3/SubFolder3/SubTest3.txt ./Folder3/SubFolder3

which makes perfect sense — it’s finding all the files at depth 3 (and beyond), stripping the last level off the pathname, and moving the file there.  But,

mv  (path_to_file)  (path_to_directory) 
means move the file into the directory.
So the command mv -i ./Folder1/SubFolder1/SubTest1.txt ./Folder1/SubFolder1 means move Folder1/SubFolder1/SubTest1.txt into Folder1/SubFolder1 — but that’s where it already is.  Therefore, you got error messages saying that you were moving a file to where it already was.

As is clear from your illustration, you want to move SubTest1.txt into Folder1.  One quick fix is

$ find . -mindepth 3 -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

which uses .. to go up from SubFolder1 to Folder1:

mv -i ./Folder1/SubFolder1/SubTest1.txt ./Folder1/SubFolder1/..
mv -i ./Folder2/SubFolder2/SubTest2.txt ./Folder2/SubFolder2/..
mv -i ./Folder3/SubFolder3/SubTest3.txt ./Folder3/SubFolder3/..

I believe that that’s bad style, although I can’t figure out quite why.  I would prefer

$ find . -mindepth 3 -type f -exec sh -c 'mv -i "$1" "${1%/*/*}"' sh {} \;

which uses %/*/* to remove two components from the pathname of the file to get what you really want,

mv -i ./Folder1/SubFolder1/SubTest1.txt ./Folder1
mv -i ./Folder2/SubFolder2/SubTest2.txt ./Folder2
mv -i ./Folder3/SubFolder3/SubTest3.txt ./Folder3

You can then use

$ find . -mindepth 2 -type d –delete

to delete the empty SubFolderN directories.  If, through some malfunction, any of them is not empty, find will leave it alone and issue a warning message.

Upvotes: 5

George Udosen
George Udosen

Reputation: 936

Let me use this example to illustrate:

Tree structure:

new
└── temp
    └── 1stlevel
        ├── 2ndlevel
        │   └── text.rtf
        └── test.txt

Move with:

find . -mindepth 4 -type f -exec mv {} ./*/* \; 

Result after move:

new
└── temp
    └── 1stlevel
        ├── 2ndlevel
        ├── test.txt
        └── text.rtf

Where you run it from matters, I am running from one folder up from the temp folder, if you want to run it from the temp folder then the command would be:

find 1stlevel/ -mindepth 2 -type f -exec mv {} ./* \;

Or:

find ./ -mindepth 3 -type f -exec mv {} ./* \;

Please look closely at the section find ./ -mindepth 3, remember that -mindepth 1 means process all files except the starting-points. So if you start from temp and are after a file in temp/1st/2nd/ then you will access it with -mindepth 3 starting at temp. Please see: man find.

Now for the destination I used ./*/*, interpretation "from current (one up from temp, mine was new) directory down to temp, then 1stlevel, so:

  • ./: => new folder
  • ./*: => new/temp folder
  • ./*/*: => new/temp/1stlevel

But all that is for the find command but another trick is to use the mv command only from the new folder:

mv ./*/*/*/* ./*/*

This is run from the new folder in my example (in other words from one folder up the temp folder). Make adjustments to run it at different levels.

To run from the temp folder:

mv ./*/*/* ./*

If your bordered about time since you mentioned you had a lot of files, then the mv option beats the find option. See the time results for just three files:

find:

real    0m0.004s
user    0m0.000s
sys     0m0.000s

mv:

real    0m0.001s
user    0m0.000s
sys     0m0.000s

Update:

Since OP wants a script to access multiple folders I came with this:

#!/usr/bin/env bash

for i in ./*/*/*;
do
        if [[ -d "$i" ]];
        then
                # Move the files to the new location
                mv "$i"/* "${i%/*}/"
                # Remove the empty directories
                rm -rf "$i"

        fi
done

How to: Run from the new folder: ./move.sh, remember to make the script executable with chmod +x move.sh.

Target directory structure:

new
├── move.sh
└── temp
    ├── folder1
    │   ├── subfolder1
    │   │   └── subtext1.txt
    │   └── test1.txt
    ├── folder2
    │   ├── subfolder2
    │   │   └── subtext2.txt
    │   └── test1.txt
    └── folder3
        ├── subfolder3
        │   └── subtext3.txt
        └── test1.txt

Get fancy result:

new
├── move.sh
└── temp
    ├── folder1
    │   ├── subtext1.txt
    │   └── text1.txt
    ├── folder2
    │   ├── subtext2.txt
    │   └── text2.txt
    └── folder3
        ├── subtext3.txt
        └── text3.txt

Upvotes: 3

Mellgood
Mellgood

Reputation: 325

mv YOUR-FILE-NAME ../

Ii thould work this way if u have writing permissions

Upvotes: 0

Related Questions