Rename multiple files based on pattern in Unix

Viewed 306769

There are multiple files in a directory that begin with prefix fgh, for example:

fghfilea
fghfileb
fghfilec

I want to rename all of them to begin with prefix jkl. Is there a single command to do that instead of renaming each file individually?

24 Answers

There are several ways, but using rename will probably be the easiest.

Using one version of rename:

rename 's/^fgh/jkl/' fgh*

Using another version of rename (same as Judy2K's answer):

rename fgh jkl fgh*

You should check your platform's man page to see which of the above applies.

This is how sed and mv can be used together to do rename:

for f in fgh*; do mv "$f" $(echo "$f" | sed 's/^fgh/jkl/g'); done

As per comment below, if the file names have spaces in them, quotes may need to surround the sub-function that returns the name to move the files to:

for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done

rename might not be in every system. so if you don't have it, use the shell this example in bash shell

for f in fgh*; do mv "$f" "${f/fgh/xxx}";done

Generic command would be

find /path/to/files -name '<search>*' -exec bash -c 'mv $0 ${0/<search>/<replace>}' {} \;

where <search> and <replace> should be replaced with your source and target respectively.

As a more specific example tailored to your problem (should be run from the same folder where your files are), the above command would look like:

find . -name 'gfh*' -exec bash -c 'mv $0 ${0/gfh/jkl}' {} \;

For a "dry run" add echo before mv, so that you'd see what commands are generated:

find . -name 'gfh*' -exec bash -c 'echo mv $0 ${0/gfh/jkl}' {} \;

This script worked for me for recursive renaming with directories/file names possibly containing white-spaces:

find . -type f -name "*\;*" | while read fname; do
    dirname=`dirname "$fname"`
    filename=`basename "$fname"`
    newname=`echo "$filename" | sed -e "s/;/ /g"`
    mv "${dirname}/$filename" "${dirname}/$newname"
done

Notice the sed expression which in this example replaces all occurrences of ; with space . This should of course be replaced according to the specific needs.

A generic script to run a sed expression on a list of files (combines the sed solution with the rename solution):

#!/bin/sh

e=$1
shift

for f in $*; do
    fNew=$(echo "$f" | sed "$e")
    mv "$f" "$fNew";
done

Invoke by passing the script a sed expression, and then any list of files, just like a version of rename:

script.sh 's/^fgh/jkl/' fgh*

This is an extended version of the find + sed + xargs solution.

Original solutions: this and this.

Requirements: search, prune, regex, rename

  1. I want to rename multiple files in many folders.
  2. Some folders should be pruned/excluded.
  3. I am on cygwin and cannot get perl rename to work, which is required for the most popular solution (and I assume it to be slow, since it does not seem to have a pruning option?)

Solution

  1. Use find to get files effectively (with pruning), and with many customization options.
  2. Use sed for regex replacement.
  3. Use xargs to funnel the result into the final command.

Example 1: rename *.js files but ignore node_modules

This example finds files and echos the found file and the renamed file. For safety reasons, it does not move anything for now. You have to replace echo with mv for that.

set -x # stop on error
set -e # verbose mode (echo all commands)

find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js" | 
  sed -nE "s/(.*)\/my(.*)/& \1\/YOUR\2/p" |
  xargs -n 2 echo  # echo first (replace with `mv` later)

The above script turns this:

./x/y/my-abc.js

Into this:

./x/y/YOUR-abc.js

Breakdown of Solution

  1. find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js"
    • Searches for files (-type f).
    • The -not part excludes (and, importantly does not traverse!) the (notoriously ginormous) node_modules folder.
    • File name must match "*.js".
    • You can add more include and exclude clauses.
    • Refs:
  2. sed -nE "s/(.*)\/my\-(.*\.js)/& \1\/YOUR-\2/p"
    • NOTE: sed always takes some getting used to.
    • -E enables "extended" (i.e. more modern) regex syntax.
    • -n is used in combination with the trailing /p flag: -n hides all results, while /p will print only matching results. This way, we only see/move files that need changing, and ignore all others.
    • Replacement regex with sed (and other regex tools) is always of the format: s/regex/replacement/FLAGS
    • In replacement, the & represents the matched input string. This will be the first argument to mv.
    • Refs:
  3. xargs -n 2 echo
    • Run the command echo with (the first two strings of) the replaced string.
    • Refs: man xargs

Good luck!

Related