129

https://serverfault.com/questions/70939/how-to-replace-a-text-string-in-multiple-files-in-linux

https://serverfault.com/questions/228733/how-to-rename-multiple-files-by-replacing-word-in-file-name

https://serverfault.com/questions/212153/replace-string-in-files-with-certain-file-extension

https://serverfault.com/questions/33158/searching-a-number-of-files-for-a-string-in-linux

These mentioned articles have all answered my question. However none of them work for me. I suspect it is because the string I am trying to replace has a # in it. Is there a special way to address this?

I have image file that had an é replaced by #U00a9 during a site migration. These look like this:

Lucky-#U00a9NBC-80x60.jpg
Lucky-#U00a9NBC-125x125.jpg
Lucky-#U00a9NBC-150x150.jpg
Lucky-#U00a9NBC-250x250.jpg
Lucky-#U00a9NBC-282x232.jpg
Lucky-#U00a9NBC-300x150.jpg
Lucky-#U00a9NBC-300x200.jpg
Lucky-#U00a9NBC-300x250.jpg
Lucky-#U00a9NBC-360x240.jpg
Lucky-#U00a9NBC-400x250.jpg
Lucky-#U00a9NBC-430x270.jpg
Lucky-#U00a9NBC-480x240.jpg
Lucky-#U00a9NBC-600x240.jpg
Lucky-#U00a9NBC-600x250.jpg
Lucky-#U00a9NBC.jpg

and I want to change it to something like this:

Lucky-safeNBC-80x60.jpg
Lucky-safeNBC-125x125.jpg
Lucky-safeNBC-150x150.jpg
Lucky-safeNBC-250x250.jpg
Lucky-safeNBC-282x232.jpg
Lucky-safeNBC-300x150.jpg
Lucky-safeNBC-300x200.jpg
Lucky-safeNBC-300x250.jpg
Lucky-safeNBC-360x240.jpg
Lucky-safeNBC-400x250.jpg
Lucky-safeNBC-430x270.jpg
Lucky-safeNBC-480x240.jpg
Lucky-safeNBC-600x240.jpg
Lucky-safeNBC-600x250.jpg
Lucky-safeNBC.jpg

UPDATE:

These examples all start with "LU00a9ucky but here are many images with different names. I am simply targeting the "#U00a9" portion of the string to replace with "safe".

6
  • 5
    So what have you actually tried? I see that you have linked to a few questions and say they failed, but how did they fail? IMO the best example uses the rename command. I suspect your rename would be as simple as rename -n 's/#/safeNBC/' *.jpg.
    – Zoredache
    Dec 19, 2014 at 21:26
  • I tried rename -n 's/#U00a9/safe/' *.jpg and the command was accepted but no changes occurred.
    – Leon Francis Shelhamer
    Dec 19, 2014 at 23:36
  • 1
    Sure, as you would have seen from the documentation you surely reviewed, the -n is the no act option. Which lets you see if it works before you actually use it. Did the output on the screen show the potential new names correctly?
    – Zoredache
    Dec 19, 2014 at 23:55
  • 1
    I apologize I copied and pasted your example without paying full attention, I did the rename command without the -n. I believe @DTK address the problem, I was not escaping the #.
    – Leon Francis Shelhamer
    Dec 20, 2014 at 8:17
  • 1
    Replacing strings in filenames on MacOS: superuser.com/questions/152627/… Nov 26, 2018 at 10:07

12 Answers 12

169

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.

9
  • 4
    This did not work on my machine (Arch Linux), but mik's answer did. Apr 21, 2017 at 8:14
  • 9
    There are two common rename utilities but neither of them are developed by GNU: Debian-based distributions include a rename utility with their Perl package while Red Hat-based distributions use the rename utility from the util-linux from the Linux Kernel Organization. Your link is to the rename C function from the GNU standard library. Jul 7, 2017 at 9:16
  • 4
    In this answer, why do we have to write /g in rename 's/#U00a9/safe/g' *
    – Porcupine
    Jul 9, 2018 at 21:25
  • 1
    waiting for the reason for /g as @Nikhil pointed
    – kokbira
    Nov 5, 2019 at 18:24
  • 4
    /g means global search, i.e. once it finds and replaces an instance of the string, it will keep searching the filename for more instances. So foo_foo.jpg would become bar_bar.jpg. If you didn't put the g, foo_foo.jpg would become bar_foo.jpg instead (only the first instance of 'foo' would change)
    – Paul Jones
    Feb 26, 2020 at 15:31
58

This is not hard, simply make sure to escape the octothorpe (#) in the name by prepending a reverse-slash (\).

find . -type f -name 'Lucky-*' | while read FILE ; do
    newfile="$(echo ${FILE} |sed -e 's/\\#U00a9/safe/')" ;
    mv "${FILE}" "${newfile}" ;
done 
1
  • Your explanation makes sense, escaping the # sounds like what I need. I do not see a backslash in your example. Should it look like this: s/\#U00a9/safe/
    – Leon Francis Shelhamer
    Dec 19, 2014 at 23:45
55

find the list of files and then replace keyword. below is example

find . -name '*jpg' -exec bash -c ' mv $0 ${0/\#U00a9NBC/safeNBC}' {} \;
6
  • 5
    You need some double-quotes around your mv arguments, in case there are spaces in the name
    – OrangeDog
    Oct 3, 2018 at 12:52
  • I needed to replace all files *_spec.rb to *_controller_spec.rb and your solution worked for me. Thanx.
    – jedi
    Sep 21, 2019 at 11:36
  • 2
    For anyone wondering, yes this is recursive.
    – Osama Dar
    Jul 17, 2020 at 11:23
  • It worked. however it is matching all other files and trying to rename/mv files. I.e mv: './jhon-654x1024-100x70.jpg' and './jhon-654x1024-100x70.jpg' are the same file. Probably need little more tweaking to skip trying for files which are outside of match
    – Arvind K.
    Sep 12, 2021 at 10:10
  • getting: new target ....... is not a directory for each file it tries to process. Jan 11, 2022 at 23:56
39

To escape # from the shell, just use single quotes ('#'), double quotes ("#"), or backslash (\#).

The simplest in your case would be to use the rename command (if it is available):

rename '#U00a9' safe *.jpg
6
  • 3
    Thanks. While the highest voted answer (rename 's/#/somethingelse/' *) didn't work on my machine (Arch Linux), this one did. Apr 21, 2017 at 8:13
  • 1
    This works using the rename command from util-linux.
    – imclean
    May 17, 2019 at 4:14
  • Any reason why neither of these work in Arch Linux in Dec 2022?
    – lockheed
    Dec 27, 2022 at 9:53
  • @lockheed, what is the error? do you have the rename executable at all?
    – mik
    Dec 30, 2022 at 13:37
  • No error. The command runs without any effect or output.
    – lockheed
    Jan 26 at 18:32
26

not sure how to in sed, but you can try this in a bash shell:

for f in Lucky-#U00a9NBC-*.jpg; do mv -v "$f" "${f/#U00a9/safe}"; done;

explanation:

  1. loops through all file names matching the glob (Lucky-#U00a9NBC-*.jpg)
  2. renames file using the move command(mv)
  3. uses native bash parameter substitution ${var/Pattern/Replacement} to craft the new name ("${f/#U00a9/safe}")

More on parameter substitution (which is highly underutilized IMO): http://www.tldp.org/LDP/abs/html/parameter-substitution.html

1
  • 1
    this one works for me, on windows using git bash terminal. none of the above worked for me Jan 11, 2022 at 23:55
1

The above examples were not working on my system (CentOS 5.6) so I found a (possibly more system-specific) command that works (note: need to escape '#' with \ on command line):

rename \#U00a9 safe *.jpg

[Also: I don't have enough reputation yet to comment, so in response to Nikhil's question regarding the use of /g in rename 's/old_string/new_string/g' (posed in the comments for another answer above):

Use the g modifier to perform a 'global' substitution (that is, to substitute new_string for old_string as many times as old_string occurs). This shouldn't be necessary in my answer because the rename will be applied to all files specified with *. See https://www.computerhope.com/unix/rename.htm for a concise explanation of this and other modifiers.]

2
  • Correct; Fedora-derived OSes like CentOS have a different version of rename than Debian-derived OSes. See unix.stackexchange.com/a/238862/135943. And by the way, CentOS 5.6 is quite old and I recommend you upgrade.
    – Wildcard
    Aug 15, 2018 at 19:23
  • Ah, thanks for the explanation + link. The outdated OS is at work and so it isn't up to me to upgrade :) but a new cluster is being built out and presumably we'll upgrade when we move over... Aug 17, 2018 at 17:02
1

Actually rename has an option exactly for that called --subst or -s in short. No need to use the regex syntax.

rename -s '#U00a9' 'safe' *

If you want to replace/substitute multi occurrences, use --subst-all or -S.

BTW, I only wanted to replace a string by nothing (remove it from file name)... well we also have an option for it -d/--delete and -D/--delete-all:

rename -d '#U00a9' *
2
  • I'd really like the last part as well. But rename does not have the -d option as far as I can see... Link
    – Klinghust
    Apr 27, 2021 at 10:22
  • 1
    @Klinghust I guess I was talking about the version of rename you get when doing brew install rename on a Mac, which is this one: formulae.brew.sh/formula/rename
    – lapin
    Sep 7 at 7:00
0

Here's DTK's solution wrapped in reusable bash function:

function renameFilesRecursively () {

  SEARCH_PATH="$1"
  SEARCH="$2"
  REPLACE="$3"

  find ${SEARCH_PATH} -type f -name "*${SEARCH}*" | while read FILENAME ; do
      NEW_FILENAME="$(echo ${FILENAME} | sed -e "s/${SEARCH}/${REPLACE}/g")";
      mv "${FILENAME}" "${NEW_FILENAME}";
  done

}

Here's how you can use it:

renameFilesRecursively /home/user/my-files apple orange
0

Follow these steps

ls Lucky-#U00a9NBC*.jpg 

Will display all jpg file names with Lucky-#U00a9NBC*

rename 's/Lucky-#U00a9NBC/Lucky-safeNBC/' *.jpg

After executing this command every Lucky-#U00a9NBC replace with Lucky-safeNBC.

 ls Lucky-safeNBC*.jpg

You can see all files are renamed

1
  • Why are you demonstrating that the pattern Lucky-#U00a9NBC*.jpg matches the names and then you don't use this fact when actually renaming the files? As far as we know, there may be other files matching the *.jpg pattern.
    – Kusalananda
    Nov 30, 2019 at 17:39
0

I'd say more likely #U00a9 is intended to be standing for the character U+00A9 here (©).

If you wanted to turn it back to ©, encoded in the locale's charset, you could do with the perl variants of rename (sometimes called prename):

rename '
  use Encode::Locale;
  use Encode;
  s/#U([0-9a-f]{4})/chr hex $1/gie;
  $_ = encode(locale_fs => $_)' ./*'#'[uU]*
0

If you are on windows using cygwin, then what works is:

find . -type f -exec rename PhraseToReplace PlaceToReplaceWith {} \;
-1

Another option is to use pyRenamer, an application made specifically for batch renaming.

It can be installed with sudo apt-get install pyrenamer

For usage details, refer to its README file on GitHub.

1
  • 3
    If you really think this could answer the question please add some explanation to your answer. Of itself, it only installs a package. You should explain what the command does and provide an example that handles the OP's specific requirement. Sep 2, 2016 at 10:02

You must log in to answer this question.