I tried running the following command:
find . -type f -exec rename -v 's/ /_/g' {} \;
, to rename all files recursively with spaces in their names with underscores. The problem is that it works sometimes and sometimes it doesn't. When it doesn't work I get the following error:
Can't rename ./dir/subdir/file name with spaces ./dir/subdir/file_name_with_spaces: No such file or directory
Anything I could be doing wrong ?
Best Answer
What is happening is that you are trying to run
rename
on a file in or below a directory with spaces in its name. Therename
utility is a bit simplistic by default and applies the given bit of Perl code to the complete argument, i.e. to the full pathname, not just to the filename portion of the pathname.Fortunately, it's easy to restrict the action to the filename portion of the pathname:
The
-execdir
predicate is non-standard, but commonly implemented. With-execdir
in place of-exec
, this executes the command with the directory where the file was found as its current directory. This way therename
utility never sees the names of the file's parent directories.I've used
--
here to protect against the event that we're trying to rename a file with a name starting with a dash (see What does "--" (double-dash) mean?). This would not be an issue with GNUfind
as the filenames will be prefixed with./
, but this does not happen on at least some implementations offind
on BSD systems.Another option is to use the
rename
utility's-d
,--filename
,--nopath
, or--nofullpath
option (these are all the same):This stops the utility from considering anything other than the filename portion of any given pathname argument.
I've taken the liberty to change the Perl expression from a
s///
command to atr///
command, since the transformation that we're performing is a simple transliteration and not a generic substitution.You may also want to only run
rename
only on filenames that contain spaces. Pick these out with a-name '* *'
test before the-exec
or-execdir
.