138

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. Commented Dec 19, 2014 at 21:26
  • 1
    I tried rename -n 's/#U00a9/safe/' *.jpg and the command was accepted but no changes occurred. Commented 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? Commented 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 #. Commented Dec 20, 2014 at 8:17
  • 1
    Replacing strings in filenames on MacOS: superuser.com/questions/152627/… Commented Nov 26, 2018 at 10:07

12 Answers 12

180

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
  • 5
    This did not work on my machine (Arch Linux), but mik's answer did. Commented 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. Commented Jul 7, 2017 at 9:16
  • 4
    In this answer, why do we have to write /g in rename 's/#U00a9/safe/g' * Commented Jul 9, 2018 at 21:25
  • 1
    waiting for the reason for /g as @Nikhil pointed Commented 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) Commented Feb 26, 2020 at 15:31
63

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 
2
  • 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/ Commented Dec 19, 2014 at 23:45
  • 1
    +1 for using 'octothorpe'! Commented May 2, 2024 at 14:14
59

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

find . -name '*jpg' -exec bash -c ' mv $0 ${0/\#U00a9NBC/safeNBC}' {} \;
6
  • 6
    You need some double-quotes around your mv arguments, in case there are spaces in the name Commented 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. Commented Sep 21, 2019 at 11:36
  • 2
    For anyone wondering, yes this is recursive. Commented 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 Commented Sep 12, 2021 at 10:10
  • getting: new target ....... is not a directory for each file it tries to process. Commented Jan 11, 2022 at 23:56
44

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. Commented Apr 21, 2017 at 8:13
  • 1
    This works using the rename command from util-linux. Commented May 17, 2019 at 4:14
  • Any reason why neither of these work in Arch Linux in Dec 2022? Commented Dec 27, 2022 at 9:53
  • @lockheed, what is the error? do you have the rename executable at all? Commented Dec 30, 2022 at 13:37
  • No error. The command runs without any effect or output. Commented Jan 26, 2023 at 18:32
32

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 Commented 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. Commented 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... Commented 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 Commented 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 Commented Sep 7, 2023 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. Commented 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. Commented Sep 2, 2016 at 10:02

You must log in to answer this question.