130

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

  • 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 '14 at 21:26
  • I tried rename -n 's/#U00a9/safe/' *.jpg and the command was accepted but no changes occurred. –  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 #. –  Dec 20 '14 at 08:17
  • 1
    Replacing strings in filenames on MacOS: https://superuser.com/questions/152627/renaming-many-files-in-mac-os-x-batch-processing – Anton Tarasenko Nov 26 '18 at 10:07
  • For arch (rename from utils-linux 2.34, check using rename -V) you can do: rename expression replacment file e.g. to replace the string foo with bar for all files you do rename 'foo' 'bar' * – xotix Jan 01 '20 at 16:43

12 Answers12

171

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.

AdminBee
  • 22,803
KrisWebDev
  • 1,913
  • 5
    This did not work on my machine (Arch Linux), but mik's answer did. – marcvangend Apr 21 '17 at 08:14
  • @marcvangend Which version of rename are you using? # sudo pacman -S perl-rename will install the perl version, which is more powerful and might be enough to make this answer work for you. – John Gowers May 20 '17 at 19:40
  • @JohnGowers Thanks! I didn't realize there are two versions. On my system, rename --version returns rename from util-linux 2.29.2 so that is indeed not the perl version. – marcvangend May 20 '17 at 21:53
  • 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. – Anthony Geoghegan Jul 07 '17 at 09:16
  • 4
    In this answer, why do we have to write /g in rename 's/#U00a9/safe/g' * – Porcupine Jul 09 '18 at 21:25
  • No need for all of that, just use option --subst or --subst-all of rename as stated in my answer – lapin Jul 16 '19 at 03:14
  • 1
    waiting for the reason for /g as @Nikhil pointed – kokbira Nov 05 '19 at 18:24
  • In my version of rename (rename from util-linux 2.23.2 from CentOS), the syntax is: rename old new files. E.g.: rename "#" "somethingelse" * – Laurent C. Jan 31 '20 at 08:18
  • 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 '20 at 15:31
59

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 
  • 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/ –  Dec 19 '14 at 23:45
54

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

find . -name '*jpg' -exec bash -c ' mv $0 ${0/\#U00a9NBC/safeNBC}' {} \;
40

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
mik
  • 1,342
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

Gregory Patmore
  • 369
  • 2
  • 4
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.]

  • Correct; Fedora-derived OSes like CentOS have a different version of rename than Debian-derived OSes. See https://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
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' *
lapin
  • 299
  • 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 '21 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: https://formulae.brew.sh/formula/rename – lapin Sep 07 '23 at 07: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

bu5hman
  • 4,756
Kalana
  • 101
  • 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]*
0

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

find . -type f -exec rename PhraseToReplace PlaceToReplaceWith {} \;
AdminBee
  • 22,803
-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.

  • 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. – Chris Davies Sep 02 '16 at 10:02