2

I would like to rename a bunch of files which are named in a general form of,

text1 text2 [firstname.lastname] text3 ... textn.ext

I want to remove the text [firstname.lastname] from the name of a file. To simplify the task, assume that [firstname.lastname] is a fixed first and last name for all the files.

I use Ubuntu 12.04, and I tried the following command,

$ find . -name "*.ext" -print | xargs sed -i 's/[firstname.lastname]//g'

but it doesn't work.

Rasoul
  • 123
  • 1
    A couple of notes: You don't have to use the -print command with find. Printing the filenames is the default behavior. you can use find's -exec flag instead of xargs: http://unix.stackexchange.com/questions/41740/find-exec-vs-find-xargs-which-one-to-choose –  Jul 03 '13 at 20:58
  • 1
    I disagree. There are different problems presented here than in the other question. Only one answer to the other question comes close to answering this question. –  Jul 04 '13 at 03:49

2 Answers2

4

Using Bash globbing:

for i in *.ext; do
    echo mv -nv -- "$i" "${i/firstname.lastname/}"
done

Remove the echo if you're happy with it.


If you need to go into subdirectories:

shopt -s globstar
for i in **/*.ext; do
    echo mv -nv -- "$i" "${i/firstname.lastname/}"
done

Remove the echo when you're happy with it.


Using find:

find . -name '*.ext' -exec bash -c 'echo mv -nv -- "$0" "${0/firstname.lastname/}"' {} \;

Remove the echo when you're happy with it.


How about a method that will rename, removing any pattern of the form xxx.yyy (so it will remove the firstname.lastname of the filename, being john.doe or james.brown): for this (using the first method):

shopt -s extglob
for i in *.ext; do
    echo mv -nv -- "$i" "${i/+([[:graph:]])\.+([[:graph]])/}"
done

or using the second method:

shopt -s globstar extglob
for i in **/*.ext; do
    echo mv -nv -- "$i" "${i/+([[:graph:]])\.+([[:graph:]])/}"
done

or using the find method:

find . -name '*.ext' -exec bash -c 'shopt -s extglob; echo mv -nv -- "$0" "${0/+([[:graph:]])\.+([[:graph:]])/}"' {} \;

If the firstname.lastname pattern is always enclosed between spaces, you could also use the pattern substitution:

"${i/*([[:space:]])+([[:graph:]])\.+([[:graph:]])*([[:space:]])/ }"

so as to only leave one space.


Note. All these methods are 100% safe regarding filenames with spaces and other funny symbols.

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
1

sed modifies a file's contents. Use rename instead.

$ find . -name "*.ext" -print0 | xargs -0 rename 's/\[firstname\.lastname\]//g'

Pass rename the -n flag to make it do a dry run. That way, you can test your command without actually renaming any files.

I also made a couple of other changes to the code.

  • Adding -print0 to find and -0 to xargs allows the command to handle spaces. By default, find uses newlines to separate the filenames it outputs and xargs expects to receive filenames separated by whitespace. -print0 and -0 make these two commands treat null bytes as filename delimiters. See this question for more information.

  • rename uses Perl regular expressions, which treat ., [, and ] specially. They must be escaped in this case.

  • I created a test file text1 [firstname.lastname].txt, executing your command generates the following error: Can't rename ./text1 /x1: No such file or directory followed by Can't rename [firstname.lastname].txt []x: No such file or directory – Rasoul Jul 03 '13 at 20:54
  • Escape the [ ] braces: \[firstname\.lastname\]. I have added that to my answer. –  Jul 03 '13 at 20:55
  • Escaping the [ and ] does not help since the problem is that find has a problem with spaces. It does not include a space as part of a file name. – Rasoul Jul 03 '13 at 20:58
  • 1
    @Rasoul: Spaces are another problem. You need to add -print0 to find (replace -print) and -0 to xargs. Read http://unix.stackexchange.com/questions/81349/how-do-i-use-find-when-the-filename-contains-spaces. –  Jul 03 '13 at 21:00
  • @Rasoul: I have edited my answer to include the extra information. –  Jul 03 '13 at 21:13