5

I have a series of files I need to rename which all have the pattern " (*)" in them. Eg;

001 (1).txt
002 (1).txt
003 (2).txt

My desired output is to rename the files to 001.txt, 002.txt, 003.txt. Note the real files vary in length and extension type (this is my test batch).

I have searched the net and SE and come up with the following which gives me the desired output in the terminal.

ls *\(*\)* | sed 's/\(.*\)\( (.*)\)\(.*\)/mv & \1\3/'

mv 001 (1).txt 001.txt
mv 002 (1).txt 002.txt
mv 003 (2).txt 003.txt

However once I pipe it to sh I get the error below.

sh: line 1: syntax error near unexpected token `('
sh: line 1: `mv 001 (1).txt 001.txt'

I know it has to do with the \( (.*)\) section. I've tried escaping the internal parentheses but then that doesn't give me the desired output. I've also searched for regex ways to say search for the whitespace followed by any 3 characters but have had no luck.

I know there are easier ways to achieve what I want but I went with sed as thus far it was the only thing I could vaguely understand. Very much appreciate any input into getting the above line working and then considering easier approaches.

Rui F Ribeiro
  • 55,929
  • 26
  • 146
  • 227
HAKS
  • 53
  • 1
  • 1
  • 3

3 Answers3

8

Using Bash

You can use this command to do it using strictly just Bash:

$ for file in ./*; do mv "$file" "${file/ (*)/}"; done

Example

$ ls -1
001 (1).txt
002 (1).txt
003 (2).txt

Running the above command:

$ for file in ./*; do mv "$file" "${file/ (*)/}"; done
$ ls -1
001.txt
002.txt
003.txt

Using rename

Some distros (Debian/Ubuntu) include the command rename. You can do what you want like so using this version of rename.

$ rename -v 's/ \(.*\)\./\./' ./*.txt

Example

$ rename -v 's/ \(.*\)\./\./' ./*.txt
001 (1).txt renamed as 001.txt
002 (1).txt renamed as 002.txt
003 (2).txt renamed as 003.txt

$ ls -1
001.txt
002.txt
003.txt

However, Red Hat distros do not include this version of rename.

References

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
slm
  • 363,520
  • 117
  • 767
  • 871
  • Thanks for providing alternate options. The first one worked as expected however the rename one wouldn't. I'll do a little research into it and see if i can work it out. – HAKS Jan 21 '14 at 21:26
  • What's the distro? – slm Jan 21 '14 at 21:29
  • you just saved my life! – Nailson Landim Jan 12 '17 at 03:43
  • Is there any way to do this recursively? – biko Oct 31 '19 at 12:12
  • @biko - what specifically are you trying to do? – slm Oct 31 '19 at 12:20
  • @slm I was trying to basically perform find and replace on filenames and directory names. I found this to work (add -type d/f for dirs/files): find . -name '*old_str *' | while read f; do mv "$f" "${f//old_str/new_str}"; done – biko Nov 01 '19 at 21:54
  • on Mac OSX on I had to escape the `(` and `)` and ended up with something like `for file in ./*; do mv "$file" "${file/ \(*\)/}"; done` – lmsurprenant Dec 05 '19 at 19:34
1

There is a rename command that does just this, but I've never used it.

Your script is close. Try this:

ls -d -- *\(*\)* | sed 's/\(.*\) (.*)\(.*\)/mv -- "&" "\1\2"/'

Note that '(' is a special character to the shell. Putting it in double-quotes cancels its special meaning.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
ash
  • 7,120
  • 2
  • 18
  • 12
1
for i in *'('*')'*
do
    mv "$i" `echo $i | sed 's/\(.*\) (.*).\(.*\)/\1.\2/'`
done
unxnut
  • 5,908
  • 2
  • 19
  • 27