Bulk renaming of camelcase files to include spaces

The problem is that the $(...) sub-shell in your command is evaluated at the time you run the command, and not evaluated by xargs for each file. You could use find's -exec instead to evaluate commands for each file in a sh, and also replace the quoting appropriately:

find . -name "*[a-z][A-Z]*" -type f -exec sh -c 'echo mv -v "{}" "$(echo "{}" | sed -E "s/([a-z])([A-Z])/\1 \2/g")"' \;

If the output of this looks good, drop the echo in echo mv. Note that due to echo | sed, this won't work with filenames with embedded \n. (But that was an already existing limitation in your own attempt, so I hope that's acceptable.)


Your sed command is taking input from the pipe. It's competing with xargs, so each of the two commands will get part of the data written by find (each byte goes to only one reader), and it's unpredictable which receives what.

You would need to pipe each file name into sed, which means you need an intermediate shell to set up the pipe.

find . -name "*[a-z][A-Z]*" -print0 | xargs -0 -I {} sh -c 'mv "$0" "$(echo "$0" | sed -E '\''s/([a-z])([A-Z])/\1 \2/g'\'')"' {}

You don't need xargs, it's mostly useless. find can call a program directly.

find . -name "*[a-z][A-Z]*" -exec sh -c 'mv "$0" "$(echo "$0" | sed -E '\''s/([a-z])([A-Z])/\1 \2/g'\'')"' {} \;

Alternatively, install any of the Perl rename implementations, for example File::Rename or the one from Unicode::Tussle.

cpan File::Rename
find -depth . -exec rename 's!([a-z])([A-Z])(?!.*/)!$1 $2!g' {} +

The (?!.*/) bit prevents the replacement from triggering in directory names, in case they contain camelcase. Passing -depth to find ensures that if a directory is renamed, this happens after find has traversed it.

Tags:

Rename

Find

Xargs