64

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".

  • 3
    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 '14 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 '14 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 '14 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 '14 at 8:17
  • Replacing strings in filenames on MacOS: superuser.com/questions/152627/… – Anton Tarasenko Nov 26 '18 at 10:07

11 Answers 11

32

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 
| improve this answer | |
  • 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 '14 at 23:45
  • @LeonFrancisShelhamer good catch. It swallowed the backslash. I'll modify. – DTK Dec 20 '14 at 1:06
97

To replace # by somethingelse for filenames in the current directory (not recursive) you can use the GNU 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, GNU rename can be installed using homebrew: brew install rename.

| improve this answer | |
  • 1
    This did not work on my machine (Arch Linux), but mik's answer did. – marcvangend Apr 21 '17 at 8:14
  • 6
    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. – Anthony Geoghegan Jul 7 '17 at 9:16
  • 3
    In this answer, why do we have to write /g in rename 's/#U00a9/safe/g' * – Nikhil Jul 9 '18 at 21:25
  • 1
    waiting for the reason for /g as @Nikhil pointed – kokbira Nov 5 '19 at 18:24
  • 1
    /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 at 15:31
30

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

find . -name '*jpg' -exec bash -c ' mv $0 ${0/\#U00a9NBC/safeNBC}' {} \;
| improve this answer | |
  • 2
    You need some double-quotes around your mv arguments, in case there are spaces in the name – OrangeDog Oct 3 '18 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 '19 at 11:36
  • Thanks. This solve my problem – Fabio Souza Nov 21 '19 at 14:53
  • For anyone wondering, yes this is recursive. – Osama Arshad Dar Jul 17 at 11:23
22

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
| improve this answer | |
  • 2
    Thanks. While the highest voted answer (rename 's/#/somethingelse/' *) didn't work on my machine (Arch Linux), this one did. – marcvangend Apr 21 '17 at 8:13
  • 1
    This works using the rename command from util-linux. – imclean May 17 '19 at 4:14
14

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

| improve this answer | |
  • While this code may answer the question, it isn’t very useful by itself. Explaining how it solves the problem would improve the usefulness and long-term value of the answer. – Anthony Geoghegan Sep 2 '16 at 10:16
  • added walkthrough explanation and further reading as per Anthony's comment. – Gregory Patmore Oct 5 '16 at 16:11
  • command line magic! thanks! – Nik Burns Dec 18 '19 at 18:02
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.]

| improve this answer | |
  • 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 '18 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... – Ezra Citron Aug 17 '18 at 17:02
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
| improve this answer | |
0

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' *
| improve this answer | |
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

| improve this answer | |
  • 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 '19 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]*
| improve this answer | |
-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.

| improve this answer | |
  • 1
    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. – roaima Sep 2 '16 at 10:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy