To replace # by somethingelse for filenames in the current directory (not recursive) you can use the (Perl-)rename utility:

rename  's/#/somethingelse/' *

Characters like - must be escaped with a \.

For your case, you would want to use

rename 's/#U00a9/safe/g' *

Note that if you only want to operate on a certain selection of files, e.g., only *.jpg, adjust the final input to match that selection:

rename 's/#U00a9/safe/g' *.jpg

To perform a test before actually changing filenames, use the -n flag:

demo/> ls                               
Lucky-#U00a9NBC-125x125.jpg  
Lucky-#U00a9NBC-150x150.jpg 

demo/> rename -n 's/#U00a9/safe/g' *.jpg
rename(Lucky-#U00a9NBC-125x125.jpg, Lucky-safeNBC-125x125.jpg)
rename(Lucky-#U00a9NBC-150x150.jpg, Lucky-safeNBC-150x150.jpg)

For OS X, rename can be installed using homebrew: brew install rename.

Answer from KrisWebDev on Stack Exchange
๐ŸŒ
PhoenixNAP
phoenixnap.com โ€บ home โ€บ kb โ€บ sysadmin โ€บ how to rename files in linux
How to Rename Files in Linux (Multiple Options and Examples)
December 9, 2025 - The last line ends the loop segment. Save the changes to the script and exit. ... Note: Learn how to compare two files using the diff command. The rename command can rename multiple files or directories in Linux.
Top answer
1 of 4
7

This will do the job:

Firstly: Install rename by running the following command in the terminal:

sudo apt install rename

Secondly: In the terminal,cd to the directory containing your files.

Finally: Rename the files to your desired format by running the following command in the terminal:

rename 's/^(.+)\.(.+)\.(.+)\.(.+)\.(.+)\.mp41$2.mp4/' *

Done


Notice:

To see how the rename command will operate on your files but without really renaming them ( just print the output to the terminal ), you can add the option -n after it. Like so:

rename -n 's/^(.+)\.(.+)\.(.+)\.(.+)\.(.+)\.mp41$2.mp4/' *

Explanation - as requested by Hamza:

Part # 1:

's/ORIGINAL/NEW/'

Substitutes the ORIGINAL string with the NEW string.

To see how it works as simple as it gets:

  • Please run in the terminal rename -n 's/file.number1.2010.720p.otherinfo.mp4/NEW.mp4/' *
  • Assuming you have a file named file.number1.2010.720p.otherinfo.mp4 in the current directory.
  • The output would be rename(file.number1.2010.720p.otherinfo.mp4, NEW.mp4)

Part # 2:

^(.+)\.(.+)\.(.+)\.(.+)\.(.+)\.mp4$

Starts at the beginning of the string ^ and then matches one or more character/s ( any character ) (.+) before the dot \.

This group is put into a variable $1 and repeated four more times ( five in total ) each group is put into a variable ( $2, $3, $4, $5 ) until .mp4 represented as \.mp4 is reached.

Makes sure the string ends with .mp4 by using the $ symbol which matches the end of the string.

This part is, however, a bit flexible and will give you undesired results if the file naming is inconsistent and you have files with more than five parts separated by dots in their names like file.number1.2010.720p.otherinfo.extrainfo.mp4

If this is the case, please substitute this part with the more strict regular expression below. This way it will only operate on files with five parts separated by dots in their names only:

^([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)\.mp4$

Part # 3:

2.mp4

Defines the new file name as what is under the variable for the first group $1 ( in this case file ) + what is under the variable for the second group $2 ( in this case number(x) ) + .mp4

Part # 4:

*

Operates on all the files in the current directory.

2 of 4
2

You can use cut and bash string manipulation:

First approach: only with cut:

for I in .*mp4
do
    echo mv "(echo $I | cut -d. -f 1,2,6)"
done 

Explanation:

  • for... will select all .mp4 files
  • echo $I | will convert the I variable into an input for the next program
  • cut -d. -f 1,2,6 will select fields 1, 2 and 6 where fields are separated by .

But this approach can be problematic since, the 6th field may be not the extension.


So you can have a second way:

for I in .*mp4
do
    echo mv "(echo $I | cut -d. -f 1,2).mp4"
done 

With that command, you force the extension to be .mp4. It will work, but what if you have not only mp4 files with the same naming pattern?


In that case, you can use extension selection:

for I in *mp4 *avi
do
    echo mv "(echo {I##*.}"
done
  • ${I##*.} will allow you to select what is behind the last . in variable I

Note: to prevent bad behavior, I added echo in the commands to see what they will do before running them really.

Find elsewhere
Top answer
1 of 4
10

If your structure only has two levels, you don't need to use recursion.

Don't parse filenames with sed. If you want to use regex and sed style syntax to rename files, use rename. If you're using Ubuntu 17.10, you need to install it

sudo apt install rename

Then you can use pa4080's answer.

With rename, use the -n option for testing.

rename -n 'expression' file(s)

You could also just use the mv command. As a one-line command:

for d in ./*/; do mv -v "$d" "${d/Edition/Volume}"; done; for f in ./*/*; do mv -v "$f" "${f/Edition/Volume}"; done

You can use echo for testing with mv, ie echo mv -v source dest, but it gives inaccurate results here, unless you test and then run the loops separately.

As a script:

#!/bin/bash

# rename directories first
for d in ./*/; do
    mv -v "$d" "${d/Edition/Volume}"
done

# now rename files
for f in ./*/*; do
    mv -v "$f" "${f/Edition/Volume}"
done

mv and rename recognise -- to indicate the end of options. Since all paths begin with ./ in this example, we do not need to use that, but if paths may begin with -, then use -- at the end of options, eg rename -n -- 's/foo/bar/' *, to prevent those paths being interpreted as options.

2 of 4
7

You could use the command rename two times to accomplish this task:

rename 's/Edition/Volume/' */        # rename the directories
rename 's/Edition/Volume/' */*.pdf   # rename the PDF files inside

Here are two similar questions:

  • Recursive bash script
  • Explaining a shell script to recursively print full directory tree
๐ŸŒ
Unix.com
unix.com โ€บ shell programming and scripting
renaming multiple files while replacing string - Shell Programming and Scripting - Unix Linux Community
October 19, 2017 - hi, i've found a few examples of scripts to do this but for some reason can't get them to work properly. basically i have some dirs with a few hundred files mixed in with a bunch of other files that were made with a typo in part of them. long-file-names-tyo-example.ext want to be able to run ...
๐ŸŒ
Linuxize
linuxize.com โ€บ home โ€บ linux commands โ€บ how to rename files and directories in linux
How to Rename Files and Directories in Linux | Linuxize
March 12, 2026 - However, it can be combined with other commands such as find or shell loops for batch renaming. ... The first line creates a for loop and iterates through a list of all files ending with .html. The second line applies to each item of the list and moves the file to a new one replacing .html with .php. The part ${f%.html} uses shell parameter expansion to remove the .html suffix from the filename. ... The find command passes all files ending with .html in the current directory to mv one by one using the -exec option. The string {} is the name of the file currently being processed.
๐ŸŒ
Ditig
ditig.com โ€บ how-to-pattern-based-rename-multiple-files
How to rename files based on pattern in Linux - Ditig
December 19, 2024 - The following shell command renames all .txt files in the current directory by replacing occurrences of the string foo with bar in their filenames.
Top answer
1 of 3
5

You can use the rename command (see edit 1).

Solution 1

For a reasonable number of files/directory, by setting bash 4 option globstar (not works on recursive name, see edit 3):

shopt -s globstar
rename -n 's/etckeeper/userkeeper/g' **

Solution 2

For a big number of files/directories using rename and find in two steps to prevent failed rename on files in just renamed directories (see edit 2):

find . -type f -exec rename 's/etckeeper/userkeeper/g' {} \;
find . -type d -exec rename 's/etckeeper/userkeeper/g' {} \;

EDIT 1:

There are two different rename commands. This answer uses the Perl-based rename command, available in Debian-based distros. To have it on a not Debian based distro you can install from cpan or grab it around.

EDIT 2:

As pointed out by Jeff Schaller in the comments the -depth find option Process each directory's contents before the directory itself so only an "ordered" find by -depth option would be enough:

find . -depth -exec rename 's/etckeeper/userkeeper/g' {} \;

EDIT 3

Solution 1 doesn't work for recursive rename targets, (es. etckeeper/etckeeper/etckeeper) becasue outer levels are processed before inner levels and pointer to inner levels become useless. (After the first rename etckeeper/etckeeper/etckeeper will be named usrkeeper/etckeeper/etckeeper so the rename for etckeeper/etckeeper/ and etckeeper/etckeeper/etckeeper will fail). The same problem fixed in find by -depth options.

EDIT4

As pointed out in the comments by cas, I'd use {} + rather than {} \; - forking a perl script like rename multiple times (once per file/dir) is expensive.

2 of 3
1

Your original question is actually pretty easy to answer: xargs (at least on OS X) has an -I option, too, which does not require that the replacement string be a unique argument. So, the invocation simply becomes:

% find . -path '*etckeeper*' -print0 | xargs -0 -n 1 -I % bash -c 'echo mv % $(echo % | sed "s/etckeeper/usrkeeper/g" )'

Easy peasy. Now, let's rename in the correct order. As it turns out, find will print directories first (because it has to process them before it descends into them), so all we need to do is reverse the order of the filenames:

% find . -path '*etckeeper*' -print | tail -r | xargs -n 1 -I % bash -c 'echo mv % $(echo % | sed "s/etckeeper/usrkeeper/g" )'

Note that I've switched find -print0 | xargs -0 for find -print | xargs. Why? Because tail -r reverses based on newlines, not null characters. If you didn't switch it, tail -r would print out the same thing you piped into it! So the script is more correct now, but also it will break on filenames that contain e.g. newlines.

Note that if you don't have tail -r, you should try tac. See: How can I reverse the order of lines in a file? on Stack Overflow.

Now, the issue is that the sed command is too aggressive. For example, with a tree like:

.
โ”œโ”€โ”€ etckeeper-foo
โ”‚   โ””โ”€โ”€ etckeeper-bar.md
โ””โ”€โ”€ some-other-directory
    โ””โ”€โ”€ etckeeper-baz.md

You'll get mv invocations that look like:

mv ./some-other-directory/etckeeper-baz.md ./some-other-directory/usrkeeper-baz.md
mv ./etckeeper-foo/etckeeper-bar.md ./usrkeeper-foo/usrkeeper-bar.md
mv ./etckeeper-foo ./usrkeeper-foo

The first one's fine, but the second one's clearly not - since we haven't yet done mv ./etckeeper-foo ./usrkeeper-foo!

Let's only replace in the last pathname component. We can do this by using basename and dirname:

% find . -name '*etckeeper*' -print | tail -r | xargs -n 1 -I % bash -c 'echo mv % $(dirname %)/$(basename % | sed "s/etckeeper/usrkeeper/g" )'

Note that the other change I've made is using find -name, not find -path.

This produces the correct mv invocations:

mv ./some-other-directory/etckeeper-baz.md ./some-other-directory/usrkeeper-baz.md
mv ./etckeeper-foo/etckeeper-bar.md ./etckeeper-foo/usrkeeper-bar.md
mv ./etckeeper-foo ./usrkeeper-foo

Finally. Remember, once again, that this will fail on esoteric filenames. Note also that if your filenames are particularly long, xargs will not work properly because the arguments to mv become too long. You can (at least on OS X) pass -x to xargs to tell it to immediately fail if this is the case.

๐ŸŒ
Baeldung
baeldung.com โ€บ home โ€บ files โ€บ how to rename multiple files in linux by removing the extension
How to Rename Multiple Files in Linux by Removing the Extension | Baeldung on Linux
March 18, 2024 - Now, weโ€™ll use the rename command to rename multiple files in Linux. For instance, to replace a part of a filename using the rename command, weโ€™ll use s/example/test/ as an argument: $ rename -v 's/example/test/' *.txt example1.txt replaced ...