Bash script deleting my files, and editing files in subdirectories question

#!/bin/bash
#
name=$1
type=$2

number=1

for file in ./**
do
	if [ $number -lt 10 ]
	then
		filenumber=00$number
	elif [ $number -lt 100 ]
	then
		filenumber=0$number
	fi

	tempname="$name""$filenumber"."$type"

	if [ -f $file ]
	then
		if [ ! -x $file ]
		then
			mv $file $tempname
		fi
	fi

	((number++))
done

So the purpose of this code is to rename files in a directory to a new name, number, and filetype. However anything that has already been renamed in my folder gets deleted. Like if I have:

file001.jpg, file002.jpg, file003.jpg, file004.jpg
newfile1, newfile2, newfile3, newfile4

when I run: ./renumber file jpg
I end up with:

file005.jpg, file006.jpg, file007.jpg, file008.jpg

and my original 4 are gone. What happened?

ALSO!
How can I get the program to also do the same to files in sub directories?

This would take some work to keep them in subdirs. I see you already use the globstar ./** for recursion but didn't include the shopt -s globstar
This is bash4 feature, so let's write bash code and use all of it's nice features:

#!/bin/bash

(( $# == 2 )) || {
        printf 'Usage: %s <prefix> <ext>\n' "$0"
        exit 1
}

prefix=$1
type=$2
number=1
shopt -s globstar

for file in ./**; do
        [ -f "$file" ] || continue # only "regular" files

        printf -v new '%s%03d.%s' "$prefix" $((number++)) "$type"

        # skip to next file if name conflict
        [ -f "$new" ] && continue
        [ -x "$file" ] && continue

        mv "$file" "$new"
        echo "$file -> $new"

done
mute@thedoctor:~/temp/TheGreatGizmo$ touch IMG_{0000..0005}.jpg more/IMG_{000..0005}.jpg
mute@thedoctor:~/temp/TheGreatGizmo$ ls
IMG_0000.jpg  IMG_0001.jpg  IMG_0002.jpg  IMG_0003.jpg  IMG_0004.jpg  IMG_0005.jpg  more  script
mute@thedoctor:~/temp/TheGreatGizmo$ ./script
Usage: ./script <prefix> <ext>
mute@thedoctor:~/temp/TheGreatGizmo$ ./script file jpg
./IMG_0000.jpg -> file001.jpg
./IMG_0001.jpg -> file002.jpg
./IMG_0002.jpg -> file003.jpg
./IMG_0003.jpg -> file004.jpg
./IMG_0004.jpg -> file005.jpg
./IMG_0005.jpg -> file006.jpg
./more/IMG_0000.jpg -> file007.jpg
./more/IMG_0001.jpg -> file008.jpg
./more/IMG_0002.jpg -> file009.jpg
./more/IMG_0003.jpg -> file010.jpg
./more/IMG_0004.jpg -> file011.jpg
./more/IMG_0005.jpg -> file012.jpg
mute@thedoctor:~/temp/TheGreatGizmo$ ls .
file001.jpg  file003.jpg  file005.jpg  file007.jpg  file009.jpg  file011.jpg  more
file002.jpg  file004.jpg  file006.jpg  file008.jpg  file010.jpg  file012.jpg  script
2 Likes

Could you avoid bash4 (and globstar) requirement by replacing:

shopt -s globstar

for file in ./**; do
        [ -f "$file" ] || continue # only "regular" files

with

find . -type f -print | while read file; do
1 Like

Sorry, I'm really new to bash. Could someone explain how each part of these code segments work in 3 year old terms? And what the second one does? I like to understand what is going on. These were taken from Neutronscott's reply. Thanks

EDIT: Okay, I see what you did here. Though I'm not sure what the "$0" is for on the end of the printf statement.

(( $# == 2 )) || {
        printf 'Usage: %s <prefix> <ext>\n' "$0"
        exit 1
}
shopt -s globstar
continue # only "regular" files

EDIT: and I understand everything here other than '%s%03d.%s'

printf -v new '%s%03d.%s' "$prefix" $((number++)) "$type"
# skip to next file if name conflict
[ -f "$new" ] && continue
[ -x "$file" ] && continue

continue just resumes at the next iteration of the enclosing for loop. In this particular code that means stop processing the current file and start processing the next file.

The current code is less than ideal as it will rename existing renamed files and skip files.

see below:

$ touch abc_{0000..0003} more/img_{0000..0003} file{000..002}.jpg
$ ls
abc_0000  abc_0001  abc_0002  abc_0003  file000.jpg  file001.jpg  file002.jpg  more  script
$ ./script file jpg
./abc_0002 -> file003.jpg
./abc_0003 -> file004.jpg
./file000.jpg -> file005.jpg
./file001.jpg -> file006.jpg
./file002.jpg -> file007.jpg
./more/img_0000 -> file008.jpg
./more/img_0001 -> file009.jpg
./more/img_0002 -> file010.jpg
./more/img_0003 -> file011.jpg
$ ls
abc_0000  file003.jpg  file005.jpg  file007.jpg  file009.jpg  file011.jpg  script
abc_0001  file004.jpg  file006.jpg  file008.jpg  file010.jpg  more

This slight update should be a little safer.

(( $# == 2 )) || {
        printf 'Usage: %s <prefix> <ext>\n' "$0"
        exit 1
}

prefix=$1
type=$2
number=1

find . -type f -print | while read file; do

        # skip already processed files
        [[ "$file" = ./$prefix[0-9][0-9][0-9].$type ]] && continue

        # skip executable files (eg this script)
        [ -x "$file" ] && continue

        # find next unused sequence
        while true
        do
            printf -v new '%s%03d.%s' "$prefix" $((++number)) "$type"
            [ -f "$new" ] || break
        done

        mv "$file" "$new"
        echo "$file -> $new"

done
1 Like