views:

218

answers:

5

How can I recursively change xxx-xxx_[a-zA-Z]+_\d+_(\d+)\.jpg into $1.jpg?

A: 

I am giving you the more verbose version that can handle any file name (even those that are in folders with white-space contaminated names):

# file rename.sh
old="$1"
new="$(dirname "$1")/$(echo "$(basename "$1")"|sed 's/^xxx-xxx_[a-zA-Z]+_[0-9]+_//')"
mv "$old" "$new"

# execute this in the shell:
find . -regex "xxx-xxx_[a-zA-Z]+_[0-9]+_[0-9]+\.jpg$" -exec ./rename.sh "{}" ";"
soulmerge
It matches a file name such as:xxx-xxx_ERR_19_01.jpgand returns 01What do I do with that code?
steven
and I don't think it does what I need:I have1/xxx-xxx_ERR_19_01.jpg2/xxx-xxx_ERR_19_01.jpg3/xxx-xxx_ERR_19_01.jpg
steven
Ah, I forgot about sub-directories in the command, will fix...
soulmerge
... fixed, need a helper file, but can handle anything now.
soulmerge
Hm.. didn't work :(The command just finished quickly. Maybe find needs an argument to be recursive?
steven
`find` is recursive by default. You have to specify options if you want it to **not** be.
Dennis Williamson
The '[0-9]+' is not a portable 'sed' regular expression. Also, you have to write the 'rename.sh' command each time you need to do one of these renaming exercises (it is customized to one replacement pattern).
Jonathan Leffler
+1  A: 
#!/bin/bash

find . | while read OLD; do
    NEW="`sed -E 's/(.*\/)?xxx-xxx_[a-zA-Z]+_[0-9]+_([0-9]+)\.jpg/\1\2.jpg/' <<< "$OLD"`"
    [ "$OLD" != "$NEW" ] && mv "$OLD" "$NEW"
done

Some notes on this:

  • Piping the output of find to a while read loop is a neat way of reading the output of find one line at a time. It's nice because it'll process the files as find finds them without having to build up the whole list of files first, as would happen if you did `find` in backticks.

  • If you have tons of unrelated files then you can add the -regex option to find as per soulmerge's answer, but if not eh, no need.

  • I modified your regex to allow for directory names at the front. They'll get captured in \1 and the number you're looking for will be \2.

  • sed <<< "$OLD" is the same thing as echo "$OLD" | sed, just a little fancier...

  • [ "$OLD" != "$NEW" ] && mv is the same thing as if [ "$OLD" != "$NEW" ]; then mv; fi, just a little fancier...

Edit: Changed \d to [0-9] for compatibility. I tested it on my Mac and it works. Here's what happened in a test directory I set up:

mv ./1/xxx-xxx_ERR_19_02.jpg ./1/02.jpg
mv ./2/xxx-xxx_BLAH_266_14.jpg ./2/14.jpg
mv ./xxx-xxx_ERR_19_01.jpg ./01.jpg
John Kugelman
How do I use it?
steven
and I don't think it works for subdirectories?
steven
`find` is recursive, so it should!
John Kugelman
use find's -name and -type f option to search for the file names instead.
ghostdog74
Are you sure it works, I just got a the help info for something scrolling down the screen real fast?
steven
It is tricky to handle file names with spaces or newlines using this technique (at best tricky; at worst, impossible).
Jonathan Leffler
A: 
find /path -type f -name "???-???_[a-zA-Z]*_[0-9]*_*.???" | while read FILE
do
  f=${FILE%%.???}  
  number=${f##*_}
  extension=${FILE##*.}
  path=${FILE%/*}
  echo "mv '$FILE' '$path/$number.$extension"
done
ghostdog74
+1  A: 

If you have the rename (or prename) command (it's a Perl script that sometimes comes with Perl):

find -type d -name dir -exec rename 's/xxx-xxx_[a-zA-Z]+_\d+_(\d+)\.jpg/$1.jpg/' {}/xxx-xxx_[a-zA-Z]*[0-9].jpg \;
Dennis Williamson
What is the rename 's/ for? I don't understand what the 's' is for?
steven
Didn't seem to work.
steven
`s/.../.../` is Perl's (and `sed`) substitute command. It searches for the regular expression between the the first and second slashes and replaces it with what's between the second and third slashes. The `$1` is a back reference to the part of the regular expression that's in parentheses (`sed` uses `\1`).
Dennis Williamson
The answer is flawed on two counts: (1) the replacement string should be '$1.jpg' instead of '$1', and (2) the rename or prename script is not distributed as standard with Perl (not on all platforms, at any rate). Nevertheless, the 'rename' Perl script is a valuable tool - see http://superuser.com/questions/63421/
Jonathan Leffler
Oops. fixed the ".jpg" part.
Dennis Williamson
+1  A: 

See the various answers to this question on SuperUser.com. It is dealing with the same issue (renaming files using a regex). [And it took me ages to find it on StackOverflow - because it wasn't on SO but on SU! :( ]

Jonathan Leffler