Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

What I am trying to do is rename folders that are anywhere in the current tree, not neccessarily directly in the working directory. What I need to do is:

  • "A 1" becomes "A 01"
  • "A 2" becomes "A 02"
  • "A 09" is ignored

I ended up with this:

find . -type d -regex '.*A [0-9]$' -execdir mv "{}" "$(echo '{}' | sed 's_[0-9]_0&_')" \;

In order to test this I reduced the complexity a bit:

find . -type d -regex '.*A [0-9]$' -execdir echo "$(echo '{}' | sed 's_[0-9]_0&_')" \;

find lists all folders I want but the pattern in sed does not seem to find a match in the folder names. It works if it is executed on its own, so I guess there is a problem with characters that need to be escaped.

share|improve this question
up vote 1 down vote accepted

Update: Added a version which can handle any/all valid filename characters, except for \x01, which is used as sed's delimiter.... I have used A.* as the pattern in the updated examples, because I was testing for A"'. Of course, just change to whatever you need.

findx="\(.*/\)\(A.* \)\([0-9]\)$"
replx="&\x00\1\20\3\x00"; I=$'\x01'
find . -depth -type d -regex "$findx" | 
  sed -n "s$I$findx$I$replx$I p" |
    tr -d '\n' | 
      xargs --verbose -0n2 mv 

Here is an example, using perl instead of sed.
Note that perl's regualar expressions PCRE are not entirely the same as posix-extended ERE as used by find in this example. They do, however, share enough in common that the same regex pattern can be used in this case.

export findx='(.*/)(A.* )([0-9])$'
find . -depth -type d -regextype posix-extended -regex "$findx" |
  perl -l000pe 's/$ENV{'findx'}/$_\000$1${2}0$3/' |
    xargs --verbose -0n2 mv 

These next versions cannot handle single-quotes ' in filenames.

Using standard regex patterns

regex='\(.*/\)\(A \)\([0-9]\)$'
find . -depth -type d -regex "$regex" |
  sed -n "s|$regex|'&' '\1\20\3'|p" |
    xargs --verbose -n2 mv 

To use extended regex paterns

regex='(.*/)(A )([0-9])$'
find . -depth -regextype posix-extended -type d -regex "$regex" |
  sed -nr "s|$regex|'&' '\1\20\3'|p" |
    xargs --verbose -n2 mv 

The -depth option is needed so that more deeply nested directories are renamed before any parent directory is renamed.

Note that the script does not test if a directory already exists with the same name as the new intended name.

share|improve this answer
Thank you very much, this works well except in a case where a ' (single quote) character is in the name. Any idea how to tell sed or xargs to escape critical characters? You don't happen to spot the error in my command using -execdir? Using xargs like you did looks much easier and clearer, but I would like to understand what's going on. – slosd Aug 12 '12 at 9:58
Your regex was basically on track, but when you start needing to echo into an echo, I find there is often another way by using a straightt pipe-line. I have added a version which caters for single and/or double quotes and any other valid character other than \x01, which is used as sed's delimiter, and \x00 which is not valid in *nix filenames. Quoting the file paths is nott needed if you use xargs with the -0, --null option as an arg-separator.. I've added an extra tr step to remove the spurious \n which sed needs. If you use perl instead of sed you could avoid this step. – Peter.O Aug 12 '12 at 18:29
Note that -execdir changes the working directory to the parent directory for each path being processed. This means that you do not need to use fully-qualified paths. But, on its own, it does not produce a -depth listing with deeper level directories before their parents. If you have /abc/A 1/B 2/ and /abc/A 1 is renamed first, then depending on how find handles its buffering, it may try to rename /abc/A 1/B 2, but that no longer exists! ... so, for this situation use -depth. – Peter.O Aug 13 '12 at 9:42
Thanks for the great effort. I must say the shell is a minefield when it comes to file renaming stuff like this. Next time I will use some higher-level language... ;) – slosd Aug 14 '12 at 17:24

Your Answer


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.