I have this list of files:

$ find .
./   sdf.sdf
./  dkfjd  L

If I do rename -n -e 's/\*|\:/_/g' -e 's/^\ +//' * I get what I want:

'   sdf.sdf' would be renamed to 'sdf.sdf'
'  dkfjd  L' would be renamed to 'dkfjd  L'
'aaa*' would be renamed to 'aaa_'
'aaa*bbb' would be renamed to 'aaa_bbb'
'aaa:bbb' would be renamed to 'aaa_bbb'

But if I do find . | rename -n -e 's/\*|\:/_/g' -e 's/^\ +//' I get:

Reading filenames from STDIN
'./aaa*' would be renamed to './aaa_'
'./aaa*bbb' would be renamed to './aaa_bbb'
'./aaa:bbb' would be renamed to './aaa_bbb'

Why does the 2nd command NOT see the directories beginning with spaces?

I also tried find . -print0 | rename -n -0 -e 's/\*|\:/_/g' -e 's/^\ +//' but that made no difference.

I need to run the rename on a large tree; is there some other way to recurse into subdirectories?

Note: ultimately I will be using this with mdfind not find so I can't use the -exec option.


1 Answer 1


This will rename files and directories (well, anything that matches, really). I've specified -depth so that higher level directories aren't renamed until the contents have been safely processed.

find . -depth | rename -n -e 'y{*:}{_}; s{(?<=/)\s+(?!.*/.*$)}{}'

The rename is built from two REs. (My rename accepts only one -e {RE} argument so I've concatenated the two operations into the single parameter.)

The first is straightforward: y{*:}{_} substitutes either * or : for _ (aka Unix tr).

The second is more complicated and uses lookbehind and lookahead to match the necessary component. It says that the expression \s+ to be processed is to be immediately preceded by /, and must not be followed by / anywhere in the rest of the filename.


rename(./  dkfjd  L, ./dkfjd  L)
rename(./aaa*, ./aaa_)
rename(./aaa*bbb, ./aaa_bbb)
rename(./   sdf.sdf, ./sdf.sdf)
rename(./aaa:bbb, ./aaa_bbb)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .