SigGP
SigGP

Reputation: 776

Bash script: one file disappears while massive renaming in loop

I am writing a short script: User chooses a folder with photos and then these photos are sorted by date of modification and renamed with pattern "PhotoX.jpg", where X is is number from 1 to total number of photos. When I execute this script first time everything is fine. The problem is, that when I try to execute this script again, on the same files, then Photo11.jpg disappeas and I don't know why. Here is code:

DIRECTORY=`zenity --file-selection --title="Wybierz miejsce docelowe" --directory`
FILES=()

eval "ls -r -path $DIRECTORY/ -t > temp.txt"
COUNTER=$(wc -l < "temp.txt")
eval mapfile -t FILES < "temp.txt"

eval "cd $DIRECTORY"
COUNTER=$((COUNTER-1))
for ((i=1;i<$COUNTER;i++))
do
    eval "mv ${FILES[$i-1]} Photo$i.jpg"
done

eval "cd"
eval "rm temp.txt"

In all files, exept for Photo11, i get information in terminal, that PhotoX.jpg and PhotoX.jpg are the same files.

Upvotes: 0

Views: 54

Answers (1)

Charles Duffy
Charles Duffy

Reputation: 295288

The code given is broken enough that it's not worth debugging -- better to rewrite.

To robustly prevent the number of output files from being less than the number of input files when input and output names can overlap, it's necessary to split the renaming into two parts: From the original name to a temporary name, and from the temporary name to the output name.

#!/bin/bash

# prompt for a directory via zenity only if not passed in on the environment
[[ -d $directory ]] || {
  directory=$(zenity --file-selection --title="Wybierz miejsce docelowe" --directory)
}

tempnames=( )

# Move files to unique destination names, prefixed with intended final name
counter=1
while IFS= read -r -d ' ' _ && IFS= read -r -d '' filename; do # read time into _
  destname="${directory}/Photo${counter}.jpg"
  [[ $filename = "$destname" ]] && continue  # skip files with correct names already
  tempname=$(mktemp -- "$destname.XXXXXX") || continue # generate a unique dest name
  mv -- "$filename" "$tempname" || {         # and rename to that...
    rm -f -- "$tempname"                     # ...deleting the tempfile and skipping the
    continue                                 #    input file if the rename somehow fails.
  }
  tempnames+=( "$tempname" )                 # ...keeping a log of what it was
  (( counter++ ))                            # finally, increment our counter.
done < <(find "$directory" -type f -printf '%T@ %p\0' | sort -zn)

# Move from temporary names to real ones
for tempname in "${tempnames[@]}"; do
  mv "$tempname" "${tempname%.*}"
done

See BashFAQ #3 to grok how we're combining GNU find and GNU sort to get a stream of files sorted by modification time.

Upvotes: 3

Related Questions