0

I have the following directory structure

root_directory
--sub_a
----index.html
--sub_b
----index.html
--sub_c
----index.css
----index.html

now I would like to move the file to the parent directory and rename it after their parent folder, but only if they're the only file in the subdirectory. This should effectively flatten out the directory, like this:

root_directory
--sub_a.html
--sub_b.html
--sub_c
----index.css
----index.html

Is there a smart way to achieve this? I can find . -name 'index.html' but I'm not sure how to additionally check if the directory includes files other than that. I guess when I have the list of those files I can move them up (mv path/index.html ..) but it's probably smarter to somehow reuse the folder name to rename them before.

1
  • Doesn't really matter for my purposes, I could remove it afterwards with find . -type d -empty -delete; I can use rename
    – Magnus
    Commented Dec 9, 2020 at 21:36

1 Answer 1

1

Assuming there is no sub-directory under sub_x directories as when if sub_x directory was contain only one regular file but one or more subdirectories or other type of files or symlinks, then they will be removed as you are requested to rename that one file with its parent directory; so, be noted.

# !/bin/bash 

find /path/to/root_directory/ -type d -execdir bash -c '
    for file in "$1"/*; do
        [[ -f "$file" ]] && (( $((++filesCount)) > 1 )) && exit 1;
    done;
    ((filesCount==1)) && echo mv -v "$file" "${file%/*}.${file##*.}" && echo rm -r "$1"
' _ {} \; 2>/dev/null

dry-run output:

mv -v ./sub_b/index.html ./sub_b.html
rm -r ./sub_b
mv -v ./sub_a/index.html ./sub_a.html
rm -r ./sub_a

remove echos in the command to perform real action of the mv and rm commands. result:

root_directory
 ├── sub_a.html
 ├── sub_b.html
 └── sub_c
     ├── index.css
     └── index.html

find ... -type d -execdir bash -c '...' _ {} \;

find only -directories and execute inline-script bash -c '...' within (usage of the -execdir action) each directory it found.


for file in "$1"/*; do
    [[ -f "$file" ]] && (( $((++filesCount)) > 1 )) && exit 1;
done;

travers all the files from the directory path returned by the $1 and checks if that's a -file then increment a variable and at the same time verifying if its value exceed more than 1 (meaning that directory have at least 2 files), if so then exit from the inline-script and find command will read next directory to continue process on; exit is used to shorten the process time and do not continue counting files in that directory as it doesn't meet our condition for that directory (directory containing one file only).


((filesCount==1)) && mv -v "$file" "${file%/*}.${file##*.}" && rm -r "$1"

checks if filesCount is equal 1 then rename the file and remove the directory; ${file%/*} and ${file##*.} used is known Shell Parameter-Expansion.

You must log in to answer this question.

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