42

I'm after a way to batch rename files with a regex i.e.

s/123/onetwothree/g

I recall i can use awk and sed with a regex but couldnt figure out how to pipe them together for the desired output.

1
  • I just wanted to thank ccpizza as the Python regex script worked great for me. I would have upvoted but I'm a noob so can't... can't do much... can't even comment. I used his Python script on my MacBook Pro to rename a load of files with capture groups, so I didn't loose any vital information and I was able to shift the filenames into a more acceptable format. I'm only just starting out with Python but love regex and this script worked great for me! Commented Feb 7, 2017 at 13:44

5 Answers 5

124

You can install perl based rename utility:

brew install rename

and than just use it like:

rename 's/123/onetwothree/g' *

if you'd like to test your regex without renaming any files just add -n switch

Sign up to request clarification or add additional context in comments.

Comments

33

An efficient way to perform the rename operation is to construct the rename commands in a sed pipeline and feed them into the shell.

ls |
sed -n 's/\(.*\)\(123\)\(.*\)/mv "\1\2\3" "\1onetwothree\2"/p' |
sh

2 Comments

The \2 after onetwothree should be \3
The only thing that worked recursively (with using the find command) $ find . -iname test* | sed -E 's/\.\/(test)(.*)(\.jpg)/mv \1\2\3 \1\214\3/' | sh
13

Namechanger is super nice. It supports regular expressions for search and replace: consider that I am doing a super complex rename with the following regex: \.sync-conflict-.*\. thats a life saver.

Regex captures groups (Diomidis answer) be the CLI way, into variables I think called $1 and $2 so rename -nv 's/^(\d{2})\.(\d{2}).*/s$1e$2.mp4/' *.mp4 becomes possible. Notice the $1 and $2? Those are coming from capture group one (\d{2}) and two (\d{2}) in my example.

enter image description here

3 Comments

Yeah.. question does say Terminal... useful nonetheless! mrrsoftware.com/namechanger
\.sync-conflict-.*\. is all im gonna say to that. perhaps with ls xargs and mv i could do similar. this app is one of those ones that makes the entire operating system worth running. a true beauty of a program.
Namechanger is a game changer! Thanks so much for the pointer. It works under MacOS 12.6 on an M2 Mac, but could use a recompilation. I'll try to contact the author.
8

My take on a friendly recursive regex file name renamer which by default only emulates the replacement and shows what the resulting file names would be.

Use -w to actually write changes when you are satisfied with the dry run result, -s to suppress displaying non-matching files; -h or --help will show usage notes.

Simplest usage:

# replace all occurences of 'foo' with 'bar'
# "foo-foo.txt" >> "bar-bar.txt"
ren.py . 'foo' 'bar' -s

# only replace 'foo' at the beginning of the filename
# "foo-foo.txt" >> "bar-foo.txt"
ren.py . '^foo' 'bar' -s

Matching groups (e.g. \1, \2 etc) are supported too:

# rename "spam.txt" to "spam-spam-spam.py"
ren.py . '(.+)\.txt' '\1-\1-\1.py' -s 

# rename "12-lovely-spam.txt" to "lovely-spam-12.txt"
# (assuming two digits at the beginning and a 3 character extension 
ren.py . '^(\d{2})-(.+)\.(.{3})' '\2-\1.\3' -s

NOTE: don't forget to add -w when you tested the results and want to actually write the changes.

Works both with Python 2.x and Python 3.x.

#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function
import argparse
import os
import fnmatch
import sys
import shutil
import re


def rename_files(args):

    pattern_old = re.compile(args.search_for)

    for path, dirs, files in os.walk(os.path.abspath(args.root_folder)):

        for filename in fnmatch.filter(files, "*.*"):

            if pattern_old.findall(filename):
                new_name = pattern_old.sub(args.replace_with, filename)

                filepath_old = os.path.join(path, filename)
                filepath_new = os.path.join(path, new_name)

                if not new_name:
                    print('Replacement regex {} returns empty value! Skipping'.format(args.replace_with))
                    continue

                print(new_name)

                if args.write_changes:
                    shutil.move(filepath_old, filepath_new)
            else:
                if not args.suppress_non_matching:
                    print('Name [{}] does not match search regex [{}]'.format(filename, args.search_for))


if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Recursive file name renaming with regex support')

    parser.add_argument('root_folder',
                        help='Top folder for the replacement operation',
                        nargs='?',
                        action='store',
                        default='.')
    parser.add_argument('search_for',
                        help='string to search for',
                        action='store')
    parser.add_argument('replace_with',
                        help='string to replace with',
                        action='store')
    parser.add_argument('-w', '--write-changes',
                        action='store_true',
                        help='Write changes to files (otherwise just simulate the operation)',
                        default=False)
    parser.add_argument('-s', '--suppress-non-matching',
                        action='store_true',
                        help='Hide files that do not match',
                        default=False)

    args = parser.parse_args(sys.argv[1:])

    print(args)
    rename_files(args)

1 Comment

Thanks @ccpizza. Worked great with Python 3 on Windows 10.
6
files = "*"
for f in $files; do
     newname=`echo "$f" | sed 's/123/onetwothree/g'`
     mv "$f" "$newname"
done

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.