Try this, based on GNU find
v4.8.0 and Bash v5.1.8
Part 1: Parse directory tree + detect sub-dir name dupes
Assume that a certain directory in your tree has the following structure:
./
|__test1/
|__dirname with space
| |__test2
| |__ test2
|__dirname **
| |__test1
|
|__reboot
| |__test1
|
|__test2/
|__test3/
|__test2/
|__test1/
|__test1/
(Strange directory names are there to demonstrate code safety.)
You see that some sub-directories (subdirs) are repeated in different ways. Some are repeated multiple times, not just once (e.g. test1
), one is not repeated (test3
), and they can be repeated either as parent and child or separated by an arbitrary number of intermediate subdirs.
The code below reveals subdir name dupes in a directory structure in a detailed way.
- it parses the file tree for the subdir structure starting from
$PWD
- it finds dupes for each components of any subdir path of 2 or more levels, not counting the root level which is
$PWD
. In my experiment, the longest subdir path is:
./test1/test2/test1/test3/test2/test1/test1
- it prints the first subdir dupe found at each subdir level, starting from the leaf, i.e. reading the subdir path right to left.
- printing is redirected toward a file, in reverse order so the longest subdir path is shown first. Two consecutive semicolons separates the path components (to the left of the ";;"), from first dupe (to the right of ";;") found according to the previous bullet.
[Code]
$ find ./* -type d -exec bash -c 'set -o noglob; IFS="/" subdir=($(printf "%s " "$1")); dirlevels=$((${#subdir[@]}-1)); dupe="$(awk '\''!($1 in sd) {sd[$1];next} {print $1}'\'' < <(printf "%s\n" ${subdir[@]:1}))";[ $dirlevels -ge 2 ] && [ ! -z "$dupe" ] && (printf "%s/" "${subdir[@]:1}";printf " ;; %s\n" "$(tail -n 1 < <(printf "%s\n" "$dupe"))";)' shellexec {} \; | tac >| tmp.data
$ cat -n tmp.data
1 test1/reboot/test1/ ;; test1
2 test1/dirname with space/test2/test2/ ;; test2
3 test1/test2/test1/test3/test2/test1/test1/ ;; test1
4 test1/test2/test1/test3/test2/test1/ ;; test1
5 test1/test2/test1/test3/test2/ ;; test2
6 test1/test2/test1/test3/ ;; test1
7 test1/test2/test1/ ;; test1
8 test1/dirname **/test1/ ;; test1
Part 2: Processing of subdir name dupes; moving contents
Processing takes place in the order displayed in tmp.data
.
- on
tmp.data
's first line, the first name dupe on the path ./test1/test2/test1/test3/test2/test1/test1
is test1
. We can transfer its contents to the left most subdir level with the same name: ./test1/
- once the contents have been moved with no clobbering of existing files at destination, the right most subdir level
test1
is deleted.
- we go on to line 2 of
tmp.data
and repeat the above steps.
- etc until all lines in
tmp.data
have been consumed.
At this stage the question (to the question's author: @TomDerks) is what to do with the right-most test1/*
on line 6 ? Should all its contents be moved to the left-most directory with the same name, which in this case is the first subdir level on the path ? Does "all" includes files in ./test1/test2/test1/
as well as the subdirectory test3
and its contents ?
The complete solution (Part 2) hinges on that.
tree -d
command - with extra parameters; for example is an XML file useful?$ tree -d -P recup.dir* --prune -o /home/tom/tmp-backup2.json -J
and$ tree -d -P recup.dir* --prune -o /home/tom/tmp-backup2.xml -X
Next step is editing those files to remove all unaffected directories en somehow loop through one of them an start moving the files./path/to/dirname/**/dirname/
to/path/to/dirname
, why do you want to do with the emptied subdirectory/path/to/dirname/**/dirname/
? 2) what if among moved files some have names that are identical to files in the destination dir ? What to do ?