5

I have a bash script that renames files in a folder according to input from a command prompt:

echo "Please rename and press enter" read rename 

if [ -z "$rename" ]; then 
  printf "no caption was selected, aborting!\n"
  exit 1
fi 

printf "rename is $rename\n" count=1

for i in *.jpg; do 
  j=printf "%04d" $count
  j="$rename"$j".jpg"
  count=$((count + 1))
  mv "$i" $j
done  
fi 
shift 
done

How can I modify this script so that the files in the folder are renamed according to their size?

If I sort the files according to size, they will look like this in the folder:

a009      978kb
a001      567kb
a003      499kb
a008      432kb

So I want the resulting files to be renamed:

a001      978kb
a002      567kb
a003      499kb
a004      432kb
whitewings
  • 2,457
  • j=printf "%04d" $count would give you an %04d: no such job error. You've forgotten $(...). Also, this line and the next could be written printf -v j "%s%04d.jpg" "$rename" "$count" in bash. – Kusalananda Apr 10 '20 at 10:01

3 Answers3

5

Something like this:

echo "Please rename and press enter"
read rename 

ls |
  # prepend filename with file size in bytes
  parallel stat -c %s,,sep,,%n --  |
  # sort by number
  sort -n |
  # rename to sequencenumber followed by size in bytes
  parallel -q --colsep ,,sep,, mv {2} "$rename"{#}_{1}
Ole Tange
  • 35,514
  • I couldn't try your solution because I can't get GNU Parallel to work with Windows Subsystem for Linux. I use WSL on a Windows 10 machine. I successfully installed GNU Parallel, but for some reason when I try to run your code I get an error saying that Parallel can't be found. – whitewings Apr 11 '20 at 02:39
  • For some reason GNU Parallel is now working on my machine. I changed sort -n to sort -n -r. sort -n was placing the biggest size files backwards, so that the biggest file was at the bottom of the renaming scheme. – whitewings Apr 11 '20 at 09:02
  • Since I'm using this in a bash script that renames the files according to my input at a command prompt I would like to remove rename the files with "a". I want the files to be renamed to whatever I type at the command prompt. This is a snippet of the bash script with your code incorporated: j="$rename"$j".jpg"; ls | parallel stat -c %s,,sep,,%n -- | sort -n -r | parallel --colsep ,,sep,, mv {2} a{#}_$j; How can I modify this so that placing an "a" in the name is removed? – whitewings Apr 11 '20 at 09:05
  • This gives me good results: g=".jpg"; j="$rename"$j".jpg"; ls | parallel stat -c %s,,sep,,%n -- | sort -n -r | parallel --colsep ,,sep,, mv {2} $rename{#}$g; done – whitewings Apr 11 '20 at 15:03
4

The main trick is to replace

    for i in *.jpg

with

    for i in $(ls -S *.jpg)

however, as Kusalananda's pointed out, this assumes "educated" file names (no spaces, no control characters), so here is a different approach:

count=1
ls -S --escape *.jpg | while read f; do
    n=$(printf "%04d" $count)
    ((count++))
    mv --no-clobber "$f" "$rename$n.jpg"
done

-S sorts by decreasing file size
--escape prevents names with embedded newlines causing damage
--no-clobber prevents overwriting destination files in case the script is run twice

P.S. Ole Tange's answer is a very nice efficient way of doing the same, but you probably won't see the difference unless you routinely rename thousands files.

2

In the zsh shell:

typeset -Z 3 count=0
for name in *.jpg(.DNOL); do
    count=$(( count + 1 ))
    mv -i -- "$name" "a$count.jpg"
done

This would iterate over all regular files with an .jpg filename suffix in the current directory, in the order of largest to smallest. For each file, it would rename it to aNNN.jpg where NNN is the zero-filled integer count of files processed so far.

The (.DNOL) glob qualifier orders the matching filenames in the correct order using OL ("revers order by length"), and selects only regular files (including hidden names) using .D ("regular files, and dot-files"). The N makes the pattern expand to nothing if there is no matching name ("null glob").

This would work on all valid filenames on any Unix system with zsh installed.

To use this from bash:

zsh -c 'typeset -Z 3 count=0
for name in *.jpg(.DNOL); do
    count=$(( count + 1 ))
    mv -i -- "$name" "a$count.jpg"
done'

If you have a variable $rename that you want to pass in as a filename prefix instead of using a:

zsh -c 'typeset -Z 3 count=0
for name in *.jpg(.DNOL); do
    count=$(( count + 1 ))
    mv -i -- "$name" "$1$count.jpg"
done' zsh "$rename"

Note that the zsh on the last line is not a typo and that the value of $rename is used as $1 inside the zsh -c script.

Kusalananda
  • 333,661