changing filenames in a directory to a number within a loop

hey guys. i'm new to shell scripting but not new to programming. i want to write a script that will take all the files in the current directory that end with a particular filetype and change all their names to a number in order. so, it would take all the jpg files and sort them in alphabetical order, then it would change their names from 1 all the way up to the end, so maybe 36 or something. whatever, until there's no more image files. this sounds like it would be a simple and short script to write, but i have no idea where to start. can anyone help me, or direct me to a similar script so i can hack it? thanks

Here is a simple model to try -

#!/bin/ksh
extension=$1
let i=1
cd /path/to/somewhere
for file in `ls *.$extension`
do
      mv $file $i.$extension
      let i=$i+1
done

usage: myscript.sh jpg

i see. so what's the $1 for? does the shell recognize that as the last characters after the last dot '.' in a filename?

also, i just want it to work for the current directory. so no reason to have a path. i guess you could, but just make it something like '$path = pwd' or something. would that work? i was thinking of using a for loop but this looks much simpler and less lines of code. i thought there would be regex involved or sed or something more complex so that it would only work on a few filetypes (images). i'd like to just type in the command while i'm in the directory i want to change and have it do it without typing in any other commands or options or parameters. what do you think?

btw i use zsh. that shouldn't make any difference right?

You said you wanted the list sorted alphabetically:

for file in $(ls *.$extension | sort); do ...

To answer your questions:

  • $1 is a positional parameter. Arguments given to a script will be placed into $1, $2, etc.
  • You can certainly work in whatever directory you choose, but "$path" does nothing and "$PATH" is something else entirely. Just leave out the "cd" command to work in the current directory.
  • If you don't want to include any other arguments, then simply hard-code the value of $extension. (hard-code? *cough*)

Welcome to the wide world of shell scripting!

bash: ( *$filetype GLOB is expanded to an alphabetic list )

filetype="jpg"
list=( *.$filetype )
for (( i = 0 ; i < ${#list[*]} ; )) ; do 
    mv ${list[$i]} $((++i)).$filetype
done

thank you for the great replies. so this is how i think it should go. the logic is there i suppose but the syntax is way off. i'm trying to create this script to work under any conditions (in any directory) and not tamper with anything but. so, i threw in a little regex, and even that is probably off, but it's to help me sleep at night. how does this look to you guys?

regex = '/[jpg]$/'
filetype = '.jpg'
list = (ls | grep $regex)
for (( i = 0 ; i < ${#list[*]} ; i++ )) ; do 
    mv ${list[$i]} $((++i)).$filetype
done

i tried yours, reborg, and it kinda messed me up a little.

kumi@throne:atlas/images/photos % sudo chmod a+x gallery
kumi@throne:atlas/images/photos % ls
10.jpg  12.jpg  14.jpg  16.jpg  18.jpg  1.jpg   21.jpg  23.jpg  25.jpg  27.jpg  29.jpg  30.jpg  32.jpg  34.jpg  36.jpg  4.jpg  6.jpg  8.jpg  gallery*
11.jpg  13.jpg  15.jpg  17.jpg  19.jpg  20.jpg  22.jpg  24.jpg  26.jpg  28.jpg  2.jpg   31.jpg  33.jpg  35.jpg  3.jpg   5.jpg  7.jpg  9.jpg
kumi@throne:atlas/images/photos % ./gallery 
mv: overwrite `1.jpg', overriding mode 0644? y
mv: cannot move `10.jpg' to `1.jpg': Permission denied
mv: overwrite `2.jpg', overriding mode 0644? 
kumi@throne:atlas/images/photos % sudo ./gallery 
kumi@throne:atlas/images/photos % ls
10.jpg  12.jpg  14.jpg  16.jpg  18.jpg  20.jpg  22.jpg  24.jpg  26.jpg  28.jpg  30.jpg  32.jpg  34.jpg  36.jpg
11.jpg  13.jpg  15.jpg  17.jpg  19.jpg  21.jpg  23.jpg  25.jpg  27.jpg  29.jpg  31.jpg  33.jpg  35.jpg  gallery*

as you can see, i lost 10 photos. why?

Interesting, I didn't know that "( *.ext )" was guaranteed to return the list in any particular order.

I think you lost photos (you tested this with samples, right?) because of a permissions problem. Check your man page for "mv", some editions have a "-f" flag or similar that answers the "mode 0644?" question. Either way, the script should exit on failure:

    mv ${list[$i]} $((++i)).$filetype || { echo "Crap!"; exit; }

hey gus. how about i write it for now to just print out the list of files and the name it will be changed to, first. that way i can see any errors before. i have copies of the photos in a different directory, so i didn't 'lose' any data, just the script doesn't work perfectly.

i like jim mcnamara's idea of adding an argument to the script so i can type the extension i want. however, i want to add in the script to match lowercase and uppercase extensions, and change them to be lowercase once they're matched and the names have been changed. how would i do this? and what do you think of this feature?

this is also something off-topic but i was wondering how to add a shellscript to the standard linux command repository so you don't have to use it in the directory only. and how do you write man-pages for your new scripts? where do they go?

bump =\ bump

(a) carefully, Linux Man Page Howto

(b) on the MANPATH

thanks for the link porter. but i'm still having trouble with my script:

kumi@throne:atlas/images/photos % ls -l
total 8148
-rwxr-xr-x 1 root root    133 2007-12-12 22:06 gallery*
-rw-r--r-- 1 kumi kumi 739930 2007-10-30 01:09 Luis Work 001.jpg
-rw-r--r-- 1 kumi kumi 760898 2007-10-30 01:10 Luis Work 010.jpg
-rw-r--r-- 1 kumi kumi 778884 2007-10-30 01:10 Luis Work 016.jpg
-rw-r--r-- 1 kumi kumi 826150 2007-10-30 01:10 Luis Work 017.jpg
-rw-r--r-- 1 kumi kumi 790932 2007-10-30 01:10 Luis Work 018.jpg
-rw-r--r-- 1 kumi kumi 687920 2007-10-30 01:10 Luis Work 028.jpg
-rw-r--r-- 1 kumi kumi 731860 2007-10-30 01:10 Luis Work 029.jpg
-rw-r--r-- 1 kumi kumi 868742 2007-10-30 01:10 Luis Work 030.jpg
-rw-r--r-- 1 kumi kumi 853904 2007-10-30 01:10 Luis Work 042.jpg
-rw-r--r-- 1 kumi kumi 725632 2007-10-30 01:10 Luis Work 070.jpg
-rw-r--r-- 1 kumi kumi 500695 2007-10-30 01:10 Pic 1.jpg
drwxr-xr-x 2 kumi kumi   4096 2007-12-12 22:06 untitled folder/
kumi@throne:atlas/images/photos % ./gallery 
mv: target `1.jpg' is not a directory
mv: target `2.jpg' is not a directory
mv: target `3.jpg' is not a directory
mv: target `4.jpg' is not a directory
mv: target `5.jpg' is not a directory
mv: target `6.jpg' is not a directory
mv: target `7.jpg' is not a directory
mv: target `8.jpg' is not a directory
mv: target `9.jpg' is not a directory
mv: target `10.jpg' is not a directory
mv: target `11.jpg' is not a directory

here's the script:

#!/bin/bash
filetype="jpg"
list=( *.$filetype )
for (( i = 0 ; i < ${#list[*]} ; )) ; do
    mv ${list[$i]} $((++i)).$filetype
done

any help?

Common pitfall. The "list=( *.$filetype )" style is quite efficient except:

  1. It screws up with filenames with spaces
  2. It screws up with empty directories

Similarly avoid "for i in ... ; do" style, when you anticipate 1 & 2 mentioned above.

A better way is manage these troubles is:

HTH

thanks for the advice buddy, but it left me in a worse position than before:

kumi@throne:atlas/images/photos % ls -a1
./
../
gallery*
.gallery.swp
.jpg
Luis Work 001.jpg
Luis Work 008.jpg
Luis Work 009.jpg
Luis Work 010.jpg
Luis Work 016.jpg
Luis Work 017.jpg
Luis Work 018.jpg
Luis Work 028.jpg
Luis Work 029.jpg
Luis Work 030.jpg
Luis Work 042.jpg
Luis Work 043.jpg
Luis Work 044.jpg
Luis Work 045.jpg
Luis Work 046.jpg
Luis Work 055.jpg
Luis Work 068.jpg
Luis Work 069.jpg
Luis Work 070.jpg
untitled folder/

kumi@throne:atlas/images/photos % ./gallery jpg
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "001.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "008.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "009.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "010.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "016.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "017.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "018.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "028.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "029.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "030.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "042.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "043.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "044.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "045.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "046.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "055.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "068.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "069.jpg"
Warning: unknown mime-type for "Luis" -- using "application/*"
Warning: unknown mime-type for "Work" -- using "application/*"
Error: no such file "Luis"
Error: no such file "Work"
Error: no such file "070.jpg"

here's the script:

#!/bin/bash

ls -1 *.jpg | sort | while read fname ; do
    print $fname
done

pretty simple script, but it's in bad shape. apparently even this method has trouble with spaces in filenames... can someone just explain to me what the problem is and why, so i can fix it myself, instead of trying scripts that don't work? please?

try this:

#!/bin/bash

ls -1 *.jpg | sort | while IFS= read fname ; do
    print "$fname"
done

while i appreciate the effort vgersh, i haven't learned much this entire thread. this began as a simple script, but now i can see how it is much more difficult. why wouldn't i just write it in perl or python?

here's what your script returned

kumi@throne:atlas/images/photos % ./gallery
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"
Error: no "print" mailcap rules found for type "image/jpeg"

still, no one has explained WHY things are not working, they just throw me scripts that they haven't tried themselves. and i'm back at square one. what kind of forum is this?

well.... we're are not your regular customer support help desk and cannot test ALL solutions..... we try to do our best - and once in awhile bugs/mispellings do slip by. You know how it goes, doncha?
If you could implement everything in say perl, why you came here asking for a shell solution to begin with? (this is a somewhat rhetorical question).

Try this:

#!/usr/local/bin/bash

ls -1 *.jpg | sort | while IFS= read fname ; do
    echo "[$fname]"
done

to answer your question, because i already know perl and python. i'd like to pick up shell scripting to automate much of my daily-weekly-and monthly tasks.

anyway i tried your script and this time it works. but it's only printing out the filenames. what i need this script to do is change all the files with a specific extension to ascending numbers with the same extension:

wonder.jpg -> 1.jpg
sally.JPG -> 2.jpg
box.jpg -> 3.jpg

and so on and so on.

so, how do i establish a variable that will increment, much like in a for loop? because i think i've got it if i can figure out how to add a variable and add 1 to it each loop.

#!/bin/bash

ls -1 *.$1 | sort | while IFS= read fname ; do
    mv $fname ${i}.${1}
done

does that look right?

Strange - you're the first person I know that goes from perl/Python/Ruby to shell scripting....
But anyhoo....
you're almost there...

#!/bin/bash
typeset -i i=1

ls -1 *.$1 | sort | while IFS= read fname ; do
     echo "mv [$fname] [{i}.${1}]"
    #mv "$fname" "${i}.${1}"
    ((i++))
done

when you're satisfied with what 'echo' outputs, comment it out, and uncomment the actual 'mv' line.

sooo it worked, at first. and i was very very excited. but when i tried the same script on the same files, it messed up. it removed like half of my images. at first i had 19 images with random names. then i ran the script and i had 19 images with names from 1.jpg - 19.jpg which is perfect. then, i ran the same script, on the same files, and it removed 1.jpg - 9.jpg..... why would it have done that? it shouldn't have done that. there's nothing in the script that i can see that would matter what the name of the file was before... what's the issue?

#!/bin/bash
typeset -i i=1

ls -a1 *.$1 | sort | while IFS= read fname ; do
    #echo "mv $fname ${i}.${1}"
    mv "$fname" "${i}.${1}"
    ((i++))
done

yeah i'm sure it's strange. i actually started out with php as my first programming language, which wasn't the best idea, but it wasn't too bad either. it provided a good transition, i will say that. i have pretty much most high-level scripting languages under my belt, except for shell scripting. also i'm trying to pick up sed and awk at the same time. those programs are friggin awesome!