With zsh and a mv implementation with support for a -n (no clobber) option, you could do:
for dir (**/unnecessary_dir_level(ND/od)) () {
(( ! $# )) || mv -n -- $@ $dir:h/ && rmdir -- $dir
} $dir/*(ND)
Where:
for var (values) cmd is the short (and more familiar among programming languages) version of the for loop.
**/: a glob operator that means any level of subdirectories
N glob qualifier: enables nullglob for that glob (don't complain if there's no match)
D glob qualifier: enables dotglob for that glob (include hidden files)
/ glob qualifier: restrict to files of type directory.
od glob qualifier: order by depth (leaves before the branch they're on)
() { body; } args: anonymous function with its args.
- here args being
$dir/*(ND): all the files including hidden ones in $dir
- the body running
mv on those files if there are any and then rmdir on the $dir that should now be empty.
$dir:h, the head of $dir (its dirname, like in csh).
Note that mv * .. is wrong on two accounts:
- it's missing the option delimiter:
mv -- * ..
- you're missing the hidden files:
mv -- *(D) .. in zsh, ((shopt -s nullglob failglob; exec mv -- * ..) in bash)
- also: you could end up losing data if there's a file with the same name in the parent.
find . -name unnecessary_dir_level -exec mv {}/* {}/.. \;
Can't work as the {}/* glob is expanded by the shell before calling mv. It would only be expanded to something if there was a directory called {} in the current directory, and then move the wrong files.
You could do something similar with find and bash with:
find . -depth -name unnecessary_dir_level -type d -exec \
bash -O nullglob -O dotglob -c '
for dir do
set -- "$dir"/*
(( ! $# )) || mv -n -- "$@" "${dir%/*}" && rmdir -- "$dir"
done' bash {} +