How to Batch Rename Files in a macOS Terminal?

138,334

Solution 1

In your specific case you can use the following bash command (bash is the default shell on macOS):

for f in *.png; do echo mv "$f" "${f/_*_/_}"; done

Note: If there's a chance that your filenames start with -, place -- before them[1]:
mv -- "$f" "${f/_*_/_}"

Note: echo is prepended to mv so as to perform a dry run. Remove it to perform actual renaming.

You can run it from the command line or use it in a script.

  • "${f/_*_/_}" is an application of bash parameter expansion: the (first) substring matching pattern _*_ is replaced with literal _, effectively cutting the middle token from the name.
  • Note that _*_ is a pattern (a wildcard expression, as also used for globbing), not a regular expression (to learn about patterns, run man bash and search for Pattern Matching).

If you find yourself batch-renaming files frequently, consider installing a specialized tool such as the Perl-based rename utility. On macOS you can install it using popular package manager Homebrew as follows:

brew install rename

Here's the equivalent of the command at the top using rename:

rename -n -e 's/_.*_/_/'  *.png

Again, this command performs a dry run; remove -n to perform actual renaming.

  • Similar to the bash solution, s/.../.../ performs text substitution, but - unlike in bash - true regular expressions are used.

[1] The purpose of special argument --, which is supported by most utilities, is to signal that subsequent arguments should be treated as operands (values), even if they look like options due to starting with -, as Jacob C. notes.

Solution 2

To rename files, you can use the rename utility:

brew install rename

For example, to change a search string in all filenames in current directory:

rename -nvs searchword replaceword *

Remove the 'n' parameter to apply the changes.

More info: man rename

Solution 3

You could use sed:

ls * | sed -e 'p;s@_.*_@_@g' | xargs -n2 mv

result:

prefix_567.png prefix_efg.png

*to do a dry-run first, replace mv at the end with echo

Explanation:

  • e: optional for only 1 sed command.
  • p: to print the input to sed, in this case it will be the original file name before any renaming
  • @: is a replacement of / character to make sed more readable. That is, instead of using sed s/search/replace/g, use s@search@replace@g
  • _.* : the underscore is an escape character to refer to the actual '.' character zero or more times (as opposed to ANY character in regex)
  • -n2: indicates that there are 2 outputs that need to be passed on to mv as parameters. for each input from ls, this sed command will generate 2 output, which will then supplied to mv.

Solution 4

I had a batch of files that looked like this: be90-01.png and needed to change the dash to underscore. I used this, which worked well:

for f in *; do mv "$f" "`echo $f | tr '-' '_'`"; done

Solution 5

you can install rename command by using brew. just do brew install rename and use it.

Share:
138,334
kidnim
Author by

kidnim

Updated on May 08, 2021

Comments

  • kidnim
    kidnim about 3 years

    I have a folder with a series of files named:

    prefix_1234_567.png
    prefix_abcd_efg.png
    

    I'd like to batch remove one underscore and the middle content so the output would be:

    prefix_567.png
    prefix_efg.png
    

    Relevant but not completely explanatory:

    • Grady Player
      Grady Player about 10 years
      look at NSString and NSMutableString's range and substring methods
    • Encryptic
      Encryptic about 10 years
      Have you considered non-terminal solutions to this? An Automator workflow can perform these types of operations with ease. You could create an automator workflow to rename all of the files and replace the text with _*_ with a blank.
    • kidnim
      kidnim about 10 years
      i thought automator couldn't do wildcards
  • mklement0
    mklement0 about 10 years
    Clever, if somewhat arcane solution. Making this more general, so as to also support filenames with embedded spaces, gets even more arcane: ls * | xargs -I % bash -c 'echo mv "%" "$(sed 's@_.*_@_@' <<<"%")"' (remove echo to actually rename).
  • kidnim
    kidnim about 10 years
    great answer, worked perfectly. i also appreciate that you went back and edited it to explain it more and provide a more elegant solution if i repeat this. again, much thanks, very helpful
  • l'L'l
    l'L'l almost 9 years
    OS X versions of GNU bash (x86_64-apple-darwin) doesn't include the rename utility.
  • David Thomas
    David Thomas almost 9 years
    Thanks, yes it needs to be installed via brew install rename
  • Steve Robbins
    Steve Robbins over 8 years
    An example using matched replacements (.* -> $1), e.g. Rename Foo bar S01E01 biz baz.ext to S01E01.ext: rename -n -e 's/.*(S[0-9]{2}E[0-9]{2}).*(\.[a-z]{2,4})/$1$2/' *
  • Sander W. van der Laan
    Sander W. van der Laan about 8 years
    This tip is awesome! I typed rename -vs GLYCOPHORIN GLYCC * and now it's automatically renaming 450+ files. And super fast too.
  • Ben Duffin
    Ben Duffin almost 8 years
    Excellent! Love the "show what would be changed" part BEFORe it actually renames things!
  • Bryan
    Bryan almost 8 years
    You can use a regex as well. rename 's/123/onetwothree/g' *
  • S.M.Mousavi
    S.M.Mousavi about 7 years
    If you like to replace any matched string use S parameter instead of s. If you like to use regex note: 1-regex is Perl-style 2-remove 's' parameter as this (rename -nv 's/ /_/g' *).
  • buckaroo1177125
    buckaroo1177125 over 6 years
    Given this was OS X, the correct installation procedure is brew install mmv.
  • James Wright
    James Wright about 6 years
    Love the idea of using echo for dry runs. Thanks for the tip!
  • Chris Redford
    Chris Redford about 4 years
    Highly recommend just doing the brew install rename
  • norbDEV
    norbDEV almost 4 years
    recoursive rename: rename -vs searchword replaceword */