The following script will search the current working directory for paths of the form
a/B/B/c and compress them to
a/B/c. This also compresses
You need GNU
find to use the
-regextype option and an implementation of
-n. If you don't have these please have a look at the unsafe posix version at the end of the script.
shopt -s dotglob failglob
find . -depth -regextype egrep -type d -regex '.*/([^/]*)/\1' -print0 |
while IFS= read -r -d '' path; do
mv -n -t "$path/.." "$path"/* &&
Arbitrary path names (whitespaces, special symbols like
*, and even linebreaks) are supported.
The command makes sure not to overwrite or delete any files. In a situation as in the left tree the repeated sub-directory has to be kept. You will get the error message
rmdir: failed to remove './A/A'. The result can be seen to the right.
. (before) . (after)
└── A └── A
├── someFile ├── someFile
├── collision ├── collision
└── A ├── anotherFile
├── collision └── A
└── anotherFile └── collision
Hidden files are copied too.
Bad Posix Version
A more portable version of the script which cannot handle line breaks inside paths, may overwrite files in situations as the one shown above, and cannot move hidden files (sub-directory is kept if there are hidden files inside).
find . -depth -type d | grep -E -x '.*/([^/]*)/\1' |
while IFS= read -r path; do
mv "$path"/* "$path/.." &&