9

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

/A/B/C/f

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...

/A/B/C/f1
/A/B/C/f2
/A/B/C/f3
/A/B/C/f4

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?

Cheers,

James

jl6
  • 1,205

4 Answers4

10

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
1

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?

Pylsa
  • 31,383
0

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.

hotei
  • 3,693
  • 2
  • 21
  • 24
0

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
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

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

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