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.