How to rename files keeping only the last N characters

How would I recursively rename

Bluebellgray 19707 Rug P1.jpg
Bluebellgray Amal 1972407 Rug R1.jpg
Bluebellgray Random 1907 Rug R2.jpg
Words Amal 1707 Rug R3.jpg
Whatever 9707 Rug R4.jpg

to

P1.jpg
R1.jpg
R2.jpg
R3.jpg
R4.jpg

I'm only interested in the last 6 characters.

I assume the answer begins with rename -n and then some funky regular expression? To get it recursive would I need to pipe and rename? I'm also only interested in .jpg files.

Thanks

1 Answer

In the following, remove the -n once you are happy about the proposed transformations.

With rename:

rename -n 's/.*(..)\.jpg$/$ *.jpg

To make it recurse into subdirectories, you have two options:

  1. use a recursive shell glob

    shopt -s globstar
    rename --nopath -n 's/.*(..)\.jpg$/$ **/*.jpg
  2. use find

    find . -name '*.jpg' -execdir rename --nopath -n 's/.*(..)\.jpg$/$ {} +

Note the addition of the --nopath option to prevent the rename command from removing the leading path elements. An alternative would be to change the leading .* of the regular expression pattern to [^/]*. Note also that the globstar version will fail if the number of matches is too large (or, more precisely, if the combined argument length exceeds ARG_MAX).


With mmv:

mmv -n '*??.jpg' '#2#3.jpg'

This one is tricky to make recursive since it does its own pattern matching. It could be wrapped in a find -type d -execdir ... command I suppose.


Here, remove the echo once you are happy about the proposed transformations.

With a simple shell loop, assuming we may remove everything up to and including the last space character:

for f in *.jpg; do echo mv -n -- "$f" "${f##* }"; done

You can make this recurse using find in the same way as for the rename command:

find . -name '*.jpg' -execdir sh -c 'for f; do echo mv -n -- "$f" "${f##* }"; done' sh {} +

Note that if you want to use the mv "$f" "${f: -6}" version suggested in Raffa's comment you would need to use one of the shells that supports negative substring indexing such as bash/ksh/zsh for the sh -c scriptlet.

You could use a recursive glob with the shell loop, but you'd need to separate the dirname and basename in order to apply the parameter substitution ex.

for f in ./**/*.jpg; do d="${f%/*}"; b="${f##*/}"; echo mv "$f" "$d/${b: -6}"; done
11

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like