Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

I have many files with .abc extension and want to change them to .edefg
How to do this from command line ?

EDIT: I have a root folder with many sub-folders, so the solution should work -r recursively.

share|improve this question
    
See askubuntu.com/questions/10607/… –  belacqua Apr 19 '11 at 22:11
add comment

4 Answers

A portable way (which will work on any POSIX compliant system):

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;

In bash4, you can use globstar to get recursive globs (**):

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

The (perl) rename command in ubuntu can rename files using perl regular expression syntax, which you can combine with globstar or the find command:

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

Also see http://mywiki.wooledge.org/BashFAQ/030

share|improve this answer
add comment

This will do the required task if all the files are in the same folder

rename 's/.abc$/.edefg/' *.abc

To rename the files recursively use this:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'
share|improve this answer
4  
Or rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abc in a modern version of Bash. –  Adam Byrtek Apr 19 '11 at 18:35
    
Great thanks Adam for giving me a tip on how to use *.abc in folders recursively! –  Rafał Cieślak Apr 19 '11 at 18:42
    
Great tip, thanks ! Where can I find more documentation about the little piece of regex's syntax ? Like what's the s at the beginning, and what other options can I use in there. Thanks ! –  Anto Mar 8 '13 at 1:23
add comment

One problem with recursive renames is that whatever method you use to locate the files, it passes the whole path to rename, not just the file name. That makes it hard to do complex renames in nested folders.

I use find's -execdir action to solve this problem. If you use -execdir instead of -exec, the specified command is run from the subdirectory containing the matched file. So, instead of passing the whole path to rename, it only passes ./filename. That makes it much easier to write the regex.

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' {} \;

In detail:

  • -type f means only look for files, not directories
  • -name '*.abc' means means only match filenames that end in .abc
  • The backslashes after -type and -name are the bash line-continuation character. I use them to make this example more readable, but they are not needed in practice.
  • However, the backslash at the end of the -execdir line is required. It is there to esacpe the semicolon, which terminates the command run by -execdir. Fun!

Explanation of the regex:

  • s/ start of the regex
  • \.\/ match the leading ./ that -execdir passes in. Use \ to escape the . and / metacharacters
  • (.+) match the filename. The parentheses capture the match for later use
  • \.abc escape the dot, match the abc
  • $ anchor the match at the end of the string

  • / marks the end of the "match" part of the regex, and the start of the "replace" part

  • version1_ add this text to every file name

  • $1 references the existing filename, because we captured it with parentheses. If you use multiple sets of parentheses in the "match" part, you can refer to them here using $2, $3, etc.
  • .abc the new file name will end in .abc. No need to escape the dot metacharacter here in the "replace" section
  • / end of the regex

Before

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc

After

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
    `-- version1_nested_file.abc

Hint: rename's -n option is useful. It does a dry run and shows you what names it will change, but does not make any changes.

share|improve this answer
add comment

Another portable way:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ {} \;
share|improve this answer
3  
Welcome to Ask Ubuntu! While this is a valuable answer, I recommend expanding it (by editing) to explain how and why that command works. –  Eliah Kagan Feb 3 '13 at 16:44
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.