157

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:

3
  • look at NSString and NSMutableString's range and substring methods Commented Jun 8, 2014 at 4:35
  • 1
    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. Commented Jun 8, 2014 at 4:40
  • 1
    i thought automator couldn't do wildcards Commented Jun 8, 2014 at 18:31

9 Answers 9

281

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.

Sign up to request clarification or add additional context in comments.

5 Comments

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
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/' *
Love the idea of using echo for dry runs. Thanks for the tip!
Highly recommend just doing the brew install rename
Google and SO still give better answers sometimes
129

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

5 Comments

This tip is awesome! I typed rename -vs GLYCOPHORIN GLYCC * and now it's automatically renaming 450+ files. And super fast too.
Excellent! Love the "show what would be changed" part BEFORe it actually renames things!
You can use a regex as well. rename 's/123/onetwothree/g' *
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' *).
recoursive rename: rename -vs searchword replaceword */
20

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.

2 Comments

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).
I found this useful for renaming and moving files that were named with \ characters (they should have been /, and frankly, in those directories instead of with descriptive names). For anyone looking for something so esoteric, it looks like ls * | sed -e 'p;s@\\@\\\\\\\\@g' | xargs -n1 echo | sed -e 'p;s@\\\\@@g' | xargs -pn2 mv, where -p displays the command to execute and asks for confirmation (y to confirm).
9

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

Comments

4

try this

for i in *.png ; do mv "$i" "${i/remove_me*.png/.png}" ; done

Here is another way:

for file in Name*.png; do mv "$file" "01_$file"; done

Comments

3

Using mmv

mmv '*_*_*' '#1_#3' *.png

1 Comment

Given this was OS X, the correct installation procedure is brew install mmv.
2

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

Comments

1

Since programmatically renaming files is risky (potentially destructive if you get it wrong), I would use a tool with a dry run mode built specifically for bulk renaming, e.g. renamer.

This command operates on all files in the current directory, use --dry-run until you're confident the output looks correct:

$ renamer --find "/(prefix_)(\w+_)(\w+)/" --replace "$1$3" -e name --dry-run *

Dry run

✔︎ prefix_1234_567.png → prefix_567.png
✔︎ prefix_abcd_efg.png → prefix_efg.png

Rename complete: 2 of 2 files renamed.

Plenty more renamer usage examples here.

Comments

1

zsh is now the default shell for macOS and it has its own bulk renaming feature, zmv which is sometimes aliased as mmv to noglob zmv -W.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.