views:

79

answers:

6

I have a large number of image files that i need to rename from the comand line. I believe the best way to do this is to use awk (please correct if this is incorrect).

file names are like the following

1038282829128738912-1.jpg

Every image file starts with '103' and I want to replace this part of the file name with '201003' - leaving the rest intact ... eg.

2010038282829128738912-1.jpg

The images are in multiple folders under one main folder (images) - but it would be handy for all the images to be copied into one folder (images_renamed)

I don't know where to start with this - and I have googled for awk usage but can only find examples of renaming text inside files.

Any help appreciated. Thanks/

+4  A: 

If you have the rename command on your UNIX, you should be able to use something like:

mkdir images_renamed
cd images_renamed
cp ../103*.jpg .
rename 103 201003 *.jpg

The rename FROM TO FILE will rename all the files specified by FILE, changing the first occurrence of FROM to TO.

If that's not available, you can use something like:

mkdir images_renamed
for fspec in 103*.jpg ; do
    cp ${fspec} images_renamed/201003${fspec:3}
done

To do this recursively, I would put it into a script with find:

#!/usr/bin/bash

rm -rf images_renamed
ls -lR images
echo

cd images
find . -name '*.jpg' | while read -r; do
    mkdir -p "../images_renamed/$(dirname "$REPLY")"
    echo 'Copying from' [$REPLY]
    echo '          to' [../images_renamed/$REPLY] and renaming.
    echo
    cp "$REPLY" "../images_renamed/$REPLY"
    cd "$(dirname "../images_renamed/$REPLY")"
    rename 103 201003 "$(basename "$REPLY")"
    cd - >/dev/null
done

cd ..
ls -lR images_renamed

Only the middle bit of that is required, the rest is for testing. The output below shows how it works, copying across every file to the new directory structure and renaming the relevant files.

images:
total 0
drwxr-xr-x+ 1 pax None 0 2010-08-12 20:55 dir1
drwxr-xr-x+ 1 pax None 0 2010-08-12 20:55 dir2
drwxr-xr-x+ 1 pax None 0 2010-08-12 20:56 dir3

images/dir1:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 20:55 102xxx.jpg
-rw-r--r-- 1 pax None 0 2010-08-12 20:55 103xxx.jpg

images/dir2:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 20:55 103yyy.jpg

images/dir3:
total 0
drwxr-xr-x+ 1 pax None 0 2010-08-12 20:55 dir 4

images/dir3/dir 4:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 20:55 103zzz.jpg

Copying from [./dir1/102xxx.jpg]
          to [../images_renamed/./dir1/102xxx.jpg] and renaming.

Copying from [./dir1/103xxx.jpg]
          to [../images_renamed/./dir1/103xxx.jpg] and renaming.

Copying from [./dir2/103yyy.jpg]
          to [../images_renamed/./dir2/103yyy.jpg] and renaming.

Copying from [./dir3/dir 4/103zzz.jpg]
          to [../images_renamed/./dir3/dir 4/103zzz.jpg] and renaming.

images_renamed:
total 0
drwxr-xr-x+ 1 pax None 0 2010-08-12 21:19 dir1
drwxr-xr-x+ 1 pax None 0 2010-08-12 21:19 dir2
drwxr-xr-x+ 1 pax None 0 2010-08-12 21:19 dir3

images_renamed/dir1:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 21:19 102xxx.jpg
-rw-r--r-- 1 pax None 0 2010-08-12 21:19 201003xxx.jpg

images_renamed/dir2:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 21:19 201003yyy.jpg

images_renamed/dir3:
total 0
drwxr-xr-x+ 1 pax None 0 2010-08-12 21:19 dir 4

images_renamed/dir3/dir 4:
total 0
-rw-r--r-- 1 pax None 0 2010-08-12 21:19 201003zzz.jpg

To flatten the file hierarchy, you can use something like:

#!/usr/bin/bash

rm -rf images_renamed
ls -lR images
echo

cd images
mkdir -p ../images_renamed
find . -name '*.jpg' | while read -r; do
    newfile="$(basename "$REPLY")"
    echo 'Copying from' [$REPLY]
    echo '          to' [../images_renamed/$newfile] and renaming.
    echo
    cp "$REPLY" "../images_renamed/$newfile"
    cd ../images_renamed
    rename 103 201003 "$newfile"
    cd - >/dev/null
done

which outputs:

cd ..
ls -lR images_renamed
images:
total 0
drwxr-xr-x+ 1 allan None 0 2010-08-12 20:55 dir1
drwxr-xr-x+ 1 allan None 0 2010-08-12 20:55 dir2
drwxr-xr-x+ 1 allan None 0 2010-08-12 20:56 dir3

images/dir1:
total 0
-rw-r--r-- 1 allan None 0 2010-08-12 20:55 102xxx.jpg
-rw-r--r-- 1 allan None 0 2010-08-12 20:55 103xxx.jpg

images/dir2:
total 0
-rw-r--r-- 1 allan None 0 2010-08-12 20:55 103yyy.jpg

images/dir3:
total 0
drwxr-xr-x+ 1 allan None 0 2010-08-12 20:55 dir 4

images/dir3/dir 4:
total 0
-rw-r--r-- 1 allan None 0 2010-08-12 20:55 103zzz.jpg

Copying from [./dir1/102xxx.jpg]
          to [../images_renamed/102xxx.jpg] and renaming.

Copying from [./dir1/103xxx.jpg]
          to [../images_renamed/103xxx.jpg] and renaming.

Copying from [./dir2/103yyy.jpg]
          to [../images_renamed/103yyy.jpg] and renaming.

Copying from [./dir3/dir 4/103zzz.jpg]
          to [../images_renamed/103zzz.jpg] and renaming.

images_renamed:
total 0
-rw-r--r-- 1 allan None 0 2010-08-12 22:41 102xxx.jpg
-rw-r--r-- 1 allan None 0 2010-08-12 22:41 201003xxx.jpg
-rw-r--r-- 1 allan None 0 2010-08-12 22:41 201003yyy.jpg
-rw-r--r-- 1 allan None 0 2010-08-12 22:41 201003zzz.jpg

but you need to keep in mind that filename clashes (the same file name under different directories) will overwrite each other.

paxdiablo
Does this work recursively?
Scytale
No, you need a find wrapped around it. Updated to show one way of doing it.
paxdiablo
Thanks for your answer. The only thing I would want to change is the destination. Instead of retaining the directory structure where the files were copied from - I want all the images to be copied to the 'images_renamed' folder. I've tried changing the script you provided but no sucess. I cant work out the relationship between $REPLY and dirname, basename etc. I can tell that $REPLY is the ful path and filename that the find command matches - but can I extract only the filename from this information for use in the rename step (ie. using only the filename, not the directory)
calumbrodie
To extract only the filename use the basename command.
dogbane
@calumbrodie, updated as per your new requirement.
paxdiablo
You're a star - thanks. I actually got to this last bit '$(basename $REPLY)' - just after I posted so I've learned something i guess.
calumbrodie
A: 

haven tested but somethin like this could work in bash

for i in `ls 103*.jpg`; do cp $i newdir/201003${i:3}; done
jartieda
This does not work with subdirectories.
Scytale
useless use of ls
ghostdog74
A: 

In ZSH you can do this (probably works in Bash too, not sure about this):

for f in **/103*.jpg; mv $f some_other_directory/201003${${f:t}#103}

Maybe I can also interest you in a tool I wrote: http://github.com/jkramer/virn It allows you to mass-rename files using Vim, including all of Vims features like regex etc. (Actually, you can use any other $EDITOR as well).

jkramer
A: 

Something like this could help:

cd images
find . -name '*.jpg' | while read -r img; do
    mkdir -p "../images_renamed/$(dirname "$img")"
    cp -v "$img" "../images_renamed/$(echo "$img" | sed -e 's/^103/201003/')"
done

Note: Not tested. Should not delete things though.

Scytale
Do you really want to make a directory for each file?
Dennis Williamson
That’s not what I do. I take the original file name (including directory) and create that directory and possible directories above it. You might want to read that line again.
Scytale
A: 

The following should work:

ls -d images*
images/          images_renamed/


for i in `find images -type f`
do
  cp $i images_renamed/$(basename $i | sed s/^103/201003/)
done
dogbane
Doesn't work if filenames contain spaces. Use `find | while read` and quote variables that contain filenames.
Dennis Williamson
agreed, but didn't feel the need to do so based on the OP's filename convention.
dogbane
A: 
find /path -type f -name "10*jpg" | sed 's/.*/mv &/' | sed 's/mv \(.*\/\)\(.[^/]*\)/& \120\2/' | sh
ghostdog74
Jesus. That’s sick. But it might actually work.
Scytale