Rename files depending on their parent directory

I have a main directory, let’s say main. This main directory has some folders, among which are three folders named ccc ddd and lll, which are the only folders I want to target and work on. Each of these subfolders has some folders inside which are all named the same in the three subfolders, so that ccc ddd lll subfolders contain folders of the same name.

Then, each of these subsubfolders that are inside ccc ddd lll has a number of files named with a certain name like this c_000 d_000 l_000, c_001 d_001 l_001 and so on.

What I want to do first is rename these files so that the directory of their two parents will be attached in the beginning of the file name like this ccc_foo1_c_000 ddd_foo1_d_000 lll_foo1_l_000 and so on. I’m asking if anyone can advise me how this can be done in a .sh script?

This is a simple tree structure of my main folder:

.
`-- main
    |-- ccc
    |   `-- foO1
    |   |    |-- c_000
    |   |    `-- c_001
    |   |
         -- foo2
    |       |-- c_000
    |       `-- c_001
    |   
    |   
    |-- ddd
    |   `-- foO1
    |   |    |-- d_000
    |   |    `-- d_001
    |   |
         -- foo2
    |       |-- d_000
    |       `-- d_001
    |-- lll
    |      `-- foO1
    |  |    |-- l_000
    |  |    `-- l_001
    |  |
    |   -- foo2
    |      |-- l_000
    |      `-- l_001
    |
    |-- aaa
    |-- bbb

Answer

You could do something like this:

for dir in ccc ddd lll; do 
    find "main/$dir" -type f -print0 | 
        while IFS= read -r -d '' f; do 
            dd=$(dirname "$f")
            new="${f/main\/}"
            new="${new//\//_}" 
            mv "$f" "$dd"/"$new"
        done
done

After the above script, your files will look like this:

.
`-- main
    |-- ccc
    |   |-- fo01
    |   |   |-- ccc_fo01_c_000
    |   |   `-- ccc_fo01_c_001
    |   `-- fo02
    |       |-- ccc_fo02_c_000
    |       `-- ccc_fo02_c_001
    |-- ddd
    |   |-- fo01
    |   |   |-- ddd_fo01_d_000
    |   |   `-- ddd_fo01_d_001
    |   `-- fo02
    |       |-- ddd_fo02_d_000
    |       `-- ddd_fo02_d_001
    `-- lll
        |-- fo01
        |   |-- lll_fo01_l_000
        |   `-- lll_fo01_l_001
        `-- fo02
            |-- lll_fo02_l_000
            `-- lll_fo02_l_001

Explanation

  • for dir in ccc ddd lll; do ...; done : do this only for these three directory names, saving each of them as $dir.
  • find "main/$dir" -type f -print0 | : find all files (type f) in main/$dir, and print them separated by the null string (-print0) to ensure that it works even if your file/directory names contain newlines.
  • while IFS= read -r -d '' f; do : this will iterate over the results of find, saving each as $f. The IFS= is needed to avoid breaking on whitespace, the -r is so that backslashes are not treated specially and the -d '' allows the reading of null-delimited input.
  • dd=$(dirname "$f") : $dd is now the directory where the current file resides. For example, main/ccc/fo01.
  • new="${f/main\/}" : this is using the shell’s string manipulation abilities to remove the string main/ from the file’s path.
  • new="${new//\//_}" : as above only now we are replacing all / with underscores. This results in a string like ccc_fo01_c000.
  • mv "$f" "$dd"/"$new" : finally, we rename the original file $f to $dd/$new. Remember that $dd is the directory this file was found in and $new is now the name you want to rename it to.

I suggest you add an echo before the mv to test before actually doing this.

Attribution
Source : Link , Question Author : Tak , Answer Author : terdon

Leave a Comment