I have a large number of small files, f, arranged in a directory structure like so:


There are 11 directories at the level of A, each with about 100 directories at the level of B, each with about 30 directories at the level of C, each with one file f.

How do I move all files f up one level? For example, given this set of files...


I want the directory /A/B/ to contain 4 files, f1 through f4. Removing directories C is not necessary.

I'm hoping this is a solved problem, possibly involving find, xargs, and whatnot. Any ideas?




It's fairly simple with GNU find (as found on Linux) or any other find that supports -execdir:

find A -type f -execdir mv -i {} .. \;

With a standard find:

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

With zsh:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

If the directory structure always has the same nesting level, you don't need any recursive traversal (but remove empty directories first):

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done

For that set of files, this would do:

$ cd /A/B/C/
$ mv ./* ../

But I'm expecting that your problem is somewhat more complicated... I can't answer to this... I'm not quite sure how your dir structure is... Could you clarify?

  • This will obviously work for any one directory /A/B/C, but as I have about 30000 such directories, I am hoping for a command that can be executed at the top of the hierarchy to take care of them all at once. – jl6 Sep 12 '10 at 22:20
  • 1
    @jl6 I see, and only files need moving? So C doesn't have to move to B, and B not to A, etc... – BloodPhilia Sep 12 '10 at 22:32
  • That's correct - the operation should only be performed on files, at the lowest level of the hierarchy, and they should only be moved one level up. – jl6 Sep 12 '10 at 23:00

My first guess is

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

as long as you don't specify -depth it should be ok. I have NOT tried it so test it first before you commit to it.

  • What does find do when there are two files wit the same name? – user16115 Sep 12 '10 at 22:26
  • Find isn't the problem, the imbedded "mv" command is. Dupe names would not be good as mv is not going to rename, it's going to overwrite. You could use mv -i to help a little, but with 30,000+ files that might be painful. If you need to have that much control over the situation you may need a program written in a scripting language (my choice would be python, YMMV). – hotei Sep 12 '10 at 22:30
  • I haven't used "backups" with mv, but looking at the man page it looks like that might be a solution to your problem of dupe names. "find A -type f -exec mv --backup numbered {} .. \;" might work (note that's a double dash in --backup) – hotei Sep 12 '10 at 22:37
  • The find example given will move all files at the lowest level to a directory one above A (I believe the ".." is getting interpreted by the shell before being passed to find/mv?). I need the files to move just one level up in the hierarchy. One further clarification: there will be no duplicate filenames, as long as the files only move ONE level up. – jl6 Sep 12 '10 at 22:57
  • @hotei: The mv commands are executed in the current directory, so .. doesn't do what you hope. See my answer for solutions. @jl6: the shell has nothing to do with .., that's handled by the kernel. – Gilles 'SO- stop being evil' Sep 12 '10 at 23:03

If you only want to move files that are in leaf directories (i.e. you don't want to move /A/B/file to /A if B contains subdirectories) then here are a couple of ways to do that:

Both require this

leaf ()
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
shopt -s nullglob

This one works:

leaf A | while read -r dir
    for file in "$dir"/*
        if [[ -e "$parent/${file##*/}" ]]
            echo "not moved: $file"
            mv "$file" "$parent"

This will be faster, but it doesn't like empty source directories:

leaf A | while read -r dir
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"

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.