Cybersecurity Updates & Tools

rename Command in Linux: Batch Rename Files with Perl Regex

The rename command in Linux renames multiple files at once using Perl regular expressions. Unlike mv, which handles one file at a time, rename applies a pattern across every matching filename in a single operation.

Two versions exist. The Perl version (available as renameprename, or file-rename) uses full Perl regex. The C version (from util-linux) uses a simpler rename oldstr newstr files syntax. To check which is installed:

bashrename --version

Output containing “File::Rename” or “perl” = Perl version. “util-linux” = C version. Install the Perl version if needed:

bashsudo apt install rename         # Ubuntu, Debiansudo dnf install prename        # Fedora, RHELsudo pacman -S perl-rename      # Arch

How to Use the rename Command in Linux

The syntax is:

bashrename [OPTIONS] perlexpr files

The most common form uses the Perl substitution operator:

bashrename 's/search/replace/' files

To change all .css files to .scss:

bashrename 's/\.css/.scss/' *.css

\.css escapes the dot because . in a regex matches any character. Without the backslash, .css would also match acss_css, or any character preceding css. The shell glob *.css expands the filenames before rename receives them.

Always test with -n before renaming. There is no undo:

bashrename -n 's/\.css/.scss/' *.css

Other useful flags:

  • -v – prints each file as it is renamed
  • -f – allows overwriting files whose target name already exists
  • -e – chains multiple expressions, applied in the order given:
bashrename -e 's/\.jpeg$/.jpg/' -e 'y/A-Z/a-z/' *.jpeg

This changes the extension first, then lowercases the full filename.

Perl Regex Patterns: s///, y///, Anchors, Flags, and Modifiers

Substitution (s///) matches a regex and replaces it:

  • s/old/new/ — replaces the first match only
  • s/old/new/g — replaces every occurrence in the filename
  • s/\.bak$// — removes .bak only at the end; $ anchors to the end of the string so .bak inside a longer name is not affected
  • s/^/prefix_/ — adds a prefix; ^ matches the start of the string
  • s/\.jpe?g$/.jpg/i — ? makes e optional; /i makes the match case-insensitive; matches .jpg.jpeg.JPG, and .JPEG

Transliteration (y///) maps characters one-to-one, similar to the shell tr command:

  • y/A-Z/a-z/ — converts each uppercase letter to lowercase
  • y/ /_/ — replaces every space with an underscore

The /e modifier evaluates the replacement as Perl code. This enables sequential numbering:

bashrename -v 'our $i; s/^/sprintf("%03d_", ++$i)/e' *.jpg

Each file gets a zero-padded three-digit prefix incremented across the batch.

Practical Examples and Handling Special Filenames

Common one-liners:

bashrename 's/\.bak$//' *.bak            # remove .bak extensionrename 's/^old_//' old_*.txt         # remove a prefixrename 's/^/project_/' *.txt         # add a prefixrename 'y/ /_/' *                    # spaces to underscoresrename 'y/A-Z/a-z/' *               # convert to lowercaserename 's/\.jpe?g$/.jpg/i' *        # normalize jpeg/JPEG to jpg

Filenames with spaces or special characters can break shell glob expansion. Use find with null-delimited output to handle them safely:

bashfind . -type f -name '*.txt' -print0 | xargs -0 rename -n 's/ /_/g'

-print0 separates filenames with null bytes instead of newlines. xargs -0 reads that null-delimited input so filenames containing spaces, newlines, or special characters reach rename intact. Remove -n once the dry run output looks correct.

Always run rename -n before committing to a batch rename. Use mv for a single file; use rename when you need a pattern applied across many filenames at once. Leave a comment below if you run into any issues.