2

I have a directory tree that looks like this (simplified, in truth there are hundreds of folders):

.
├── a
│   └── b
│       └── a.h5
├── b
│   ├── b
│   │   └── e.h5
│   └── c
│       ├── a.h5
│       └── b.h5
├── c
│   └── b
│       └── a.h5
├── d
│   ├── a.h5
│   └── e.h5
└── e
    └── e.h5

Basically, some .h5 files are at depth 1 (ex. d/a.h5, e/e.h5) and some files are are depth 2 (ex. b/b/e.h5, b/c/b.h5, ...)

I would like to move up only the files at depth 2, so that they all lie at depth one, as in:

.
├── a
│   └── a.h5
├── b
│   ├── e.h5
│   ├── a.h5
│   └── b.h5
├── c
│   └── a.h5
├── d
│   ├── a.h5
│   └── e.h5
└── e
    └── e.h5

I know that */*/*.h5 matches the files i'm interested in (tested via ls */*/*.h5), but I tried mv */*/*.h5 */*.h5 and it made a mess.

Duplicate files would ideally be handled by renaming, but also prompting is okay. How should I procede?

P.S: I've seen:

But they both apply to single directories.

3 Answers 3

4

This should be a start:

find . -mindepth 3 -maxdepth 3 -type f -execdir mv -i -v {} .. \;

the mv -i asks to overwrite existing files. -execdir changes to the directory of the file before doing the command.

3

Going from your directory structure:

$ tree
.
├── a
│   └── b
│       └── a.h5
├── b
│   ├── b
│   │   └── e.h5
│   └── c
│       ├── a.h5
│       └── b.h5
├── c
│   └── b
│       └── a.h5
├── d
│   ├── a.h5
│   └── e.h5
└── e
    └── e.h5

12 directories, 8 files

Use this for loop:

$ for f in */*/*.h5; do mv -v -- "$f" "${f%/*}/../"; done
»a/b/a.h5“ -> »a/b/../a.h5“
»b/b/e.h5“ -> »b/b/../e.h5“
»b/c/a.h5“ -> »b/c/../a.h5“
»b/c/b.h5“ -> »b/c/../b.h5“
»c/b/a.h5“ -> »c/b/../a.h5“

The result:

$ tree
.
├── a
│   ├── a.h5
│   └── b
├── b
│   ├── a.h5
│   ├── b
│   ├── b.h5
│   ├── c
│   └── e.h5
├── c
│   ├── a.h5
│   └── b
├── d
│   ├── a.h5
│   └── e.h5
└── e
    └── e.h5

12 directories, 8 files

Explanation:

for f in */*/*.h5; do mv -v -- "$f" "${f%/*}/../"; done
  • for f in */*/*.h5 loops trough all the desired files
    • mv -v -- moves them, verbosely. -- prevents filenames with dashes to be interpreted as arguments.
    • "$f" the original filename
    • ${f%/*}/../ the name of the directory containing the file, ../ added. That path is interpreted as "one directory up".
1

try

 for x in */*/*.h5
 do
    mv "$x" "$(dirname $(dirname $x))"
 done

if you have no strange characters in your directory names.

Or, alternatively:

ls */*/*.h5 | awk -F/ '{print "mv \"%s\"  \"%s/%s\" \n",$0,$1,$3 ;}' | bash

you can remove | bash to get a preview.

1
  • Your for loop can deal with any file name, why did you add the extremely fragile awk/ls one?
    – terdon
    Oct 27, 2015 at 13:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .