Search code examples
bashshellfile-renamequotingsubdirectory

bash rename file/add integer in filename with various extensions in multiple subdirectories


I want to insert an integer into filenames with various extensions in multiple subdirectories using bash.

Examples:

  • ./trial2/foo.hhh --> ./trial2/foo1.hhh
  • ./trial2/trial3/foo.txt--> ./trial2/trial3/foo1.txt

I tried to separate the filename from the extension and insert the integer in between with:

i=123
find . -type f -exec sh -c ' echo mv "$0" "${0%.*}${i}.${0##*.}" ' {} \;
mv ./trial2/foo.hhh ./trial2/foo.hhh

But the variable output ${i} is not printed. Why?
If I change to:

find . -type f -exec sh -c ' mv "$0" "${0%.*}123.${0##*.}" ' {} \;
mv ./trial2/foo.hhh ./trial2/foo123.hhh

The number is printed. However, I need the variable ${i} as it will be defined in a wrapping for-loop.


Solution

  • Edit

    You are almost there; you just hit a quoting subtlety. i is declared in the current shell, so needs to be passed into the sh run by find somehow. In your current command, ${i} is in single quotes, so is not interpreted by bash before the quoted material is passed to sh.

    As suggested by Charles Duffy:

    find . -type f -exec sh -c ' echo mv "$0" "${0%.*}${1}.${0##*.}" ' {} "$i" \;
    

    Within the sh command, $0 is the {}, as you know. $1 is the second parameter to the sh, which is "$i" (i expanded as a single word). Instead of ${i}, the sh command uses ${1} to access that parameter (a copy of i).

    Original

    In this example, i is interpolated in the current shell.

    Before: find . -type f -exec sh -c ' echo mv "$0" "${0%.*}${i}.${0##*.}" ' {} \;
    After:  find . -type f -exec sh -c ' echo mv "$0" "${0%.*}'"${i}"'.${0##*.}" ' {} \;
                                                              ^^    ^^
    

    The '"${i}"' drops you out of the single quotes, then expands i, then takes you back into the single quotes. That way the command you are passing to sh includes the value of i you want.