0

How do I fix this script so that it uses the user-input STRING to rename all files in the current directory, and all files in sub-directories?

Current filenames could be anything. I want the ending result for all files to be called STRING_001.jpg STRING_002.jpg STRING_003.jpg etc...

It does NOT matter to me, if the files are only sequential in their respective directories, or if the file number carries over to the next directory.

This is the code I ended up writing, after a few hours of research:

#!/bin/bash

# Let user input a string to use in filenames
read -p "Enter filename: " STRING

# Variable for sequential filenames
n=0

files=(./*)

# Looks for files in current directory
if [ ${#files[@]} -gt 0 ]; 
then
        for f in ./*; do
                  # Renames files with STRING and sequentially
                printf -v num %03d "$((++i))"
                mv "$f" "${STRING}_$num.jpg" 
        done
else 
        for d in *; do
        ( cd -- "$d" && 
                for f in ./*; do 
                          # Executes rename command in sub-directories
                        printf -v num %03d "$((++i))"
                        mv "$f" "${STRING}_$num.jpg" 
                done 
        )
done;
fi
  • https://stackoverflow.com/questions/3211595/renaming-files-in-a-folder-to-sequential-numbers – falm Jun 25 '20 at 02:20
  • To Almaz, the answer you linked does not rename files recursively. It only works in the current directory, and I've already figure that part out, if you look at my code. – Marslander Jun 25 '20 at 03:40
  • Why not just use `find . -type f | while read infile ; do ...`? – r2evans Jun 25 '20 at 03:47

1 Answers1

1

Script:

#!/bin/bash
read -p "Enter filename: " STRING
find . -type f | cat -n - | while read num infile ; do
    filename=$(basename "${infile}")
    filesans="${filename%%.*}"
    newname=$(printf '%s_%03d' "${STRING}" "${num}")
    newfile="${infile/$filesans/$newname}"
    echo "mv \"${infile}\" \"${newfile}\""
done

(Here, this script is named 62566825.sh. Not the greatest name.)

Setup the directory:

mkdir dir1 dir2 dir3
touch dir1/file{1,2,3} dir2/file{4,5,6}.jpg dir3/file{7,8,9}.foo.gz

$ find . -type f
./62566825.sh
./dir1/file1
./dir1/file2
./dir1/file3
./dir2/file4.jpg
./dir2/file5.jpg
./dir2/file6.jpg
./dir3/file7.foo.gz
./dir3/file8.foo.gz
./dir3/file9.foo.gz

Execution:

$ ./62566825.sh
Enter filename: QUUX
mv "./62566825.sh" "./QUUX_001.sh"
mv "./dir1/file1" "./dir1/QUUX_002"
mv "./dir1/file2" "./dir1/QUUX_003"
mv "./dir1/file3" "./dir1/QUUX_004"
mv "./dir2/file4.jpg" "./dir2/QUUX_005.jpg"
mv "./dir2/file5.jpg" "./dir2/QUUX_006.jpg"
mv "./dir2/file6.jpg" "./dir2/QUUX_007.jpg"
mv "./dir3/file7.foo.gz" "./dir3/QUUX_008.foo.gz"
mv "./dir3/file8.foo.gz" "./dir3/QUUX_009.foo.gz"
mv "./dir3/file9.foo.gz" "./dir3/QUUX_010.foo.gz"

(Just fix the echo "mv to actually do something.)

r2evans
  • 141,215
  • 6
  • 77
  • 149
  • Your code works perfectly, I just can't figure out how to disable dry run. You said to fix the `echo "mv` part, but isn't that line just for show, like verbose in commands? And where can I learn to code bash scripts like that? – Marslander Jun 25 '20 at 05:01
  • Change `echo "mv \"${infile}\" \"${newfile}\""` to `mv "${infile}" "${newfile}"` – r2evans Jun 25 '20 at 05:02