To guard against deleting empty subdirectories, IMO the simplest way is to move to a temporary directory and then rename the temporary directory to the old one. Say the directory in question is foo, and you want foo/*/* to become foo/*. Do something like:
tar c --remove-files foo | tar xv --strip-components=2 --backup=t --one-top-level=temp
mv temp foo
- With
--remove-files, the first tar will delete the files as it processes them, finally deleting foo itself.
- With
--strip-components=2, the second tar will chop off the foo/*/ from paths it is extracting, so foo/a/b will become b.
--backup=t is the same as with cp or mv - make numbered backup copies. However, mv makes copies of directories if they have the same name, but tar will merge the directories and make backups of files.
--one-top-level=temp tells tar to create a directory named temp and extract files there.
For example:
$ tree bar
bar
├── a
│ ├── c
│ │ └── bar
│ ├── d
│ │ └── bar
│ ├── e
│ │ └── bar
│ └── f
│ └── bar
├── b
│ ├── c
│ │ └── bar
│ ├── d
│ │ └── bar
│ ├── e
│ │ └── bar
│ └── f
│ └── bar
└── c
├── c
│ └── bar
├── d
│ └── bar
├── e
│ └── bar
└── f
└── bar
And after running the tar commands:
$ tar c --remove-files bar | tar xv --strip-components=2 --backup=t --one-top-level=temp
bar/a/e/
bar/a/e/bar
bar/a/f/
bar/a/f/bar
bar/a/c/
bar/a/c/bar
bar/a/d/
bar/a/d/bar
bar/b/e/
bar/b/e/bar
Renaming ‘temp/e/bar’ to ‘temp/e/bar.~1~’
bar/b/f/
bar/b/f/bar
Renaming ‘temp/f/bar’ to ‘temp/f/bar.~1~’
bar/b/c/
bar/b/c/bar
Renaming ‘temp/c/bar’ to ‘temp/c/bar.~1~’
bar/b/d/
bar/b/d/bar
Renaming ‘temp/d/bar’ to ‘temp/d/bar.~1~’
bar/c/e/
bar/c/e/bar
Renaming ‘temp/e/bar’ to ‘temp/e/bar.~2~’
bar/c/f/
bar/c/f/bar
Renaming ‘temp/f/bar’ to ‘temp/f/bar.~2~’
bar/c/c/
bar/c/c/bar
Renaming ‘temp/c/bar’ to ‘temp/c/bar.~2~’
bar/c/d/
bar/c/d/bar
Renaming ‘temp/d/bar’ to ‘temp/d/bar.~2~’
$ tree temp
temp
├── c
│ ├── bar
│ ├── bar.~1~
│ └── bar.~2~
├── d
│ ├── bar
│ ├── bar.~1~
│ └── bar.~2~
├── e
│ ├── bar
│ ├── bar.~1~
│ └── bar.~2~
└── f
├── bar
├── bar.~1~
└── bar.~2~
4 directories, 12 files
With the tar output, you can see when tar renamed files for backing up, allowing you track which files became which backups.
I'm mildly surprised that it works, but you can actually provide the same directory name for --one-top-level and eliminate need to rename. Just this pipeline is enough:
tar c --remove-files bar | tar xv --strip-components=2 --backup=t --one-top-level=bar