I HATE spaces in file names
Bash 116 bytes, 16 spaces
find . -depth -exec bash -c 'B=${0##*/}
M="${0%/*}/${B// /_}"
while [ -e "$M" ]
do M=$M.
done
mv "$0" "$M"' {} \;
I didn't suppress errors to gain a couple more bytes. This will not have any collisions.
If non-posix GNU find
can be expected, this can be shortened further:
Bash 110 bytes, 15 spaces
find -d -exec bash -c 'B=${0##*/}
M="${0%/*}/${B// /_}"
while [ -e "$M" ]
do M=$M.
done
mv "$0" "$M"' {} \;
Removing spaces instead of replacing them uses two less bytes:
Bash 108 bytes, 15 spaces
find -d -exec bash -c 'B=${0##*/}
M="${0%/*}/${B// }"
while [ -e "$M" ]
do M=$M.
done
mv "$0" "$M"' {} \;
Note: if tabs can be used instead of spaces, only 1 space is needed (the one in the match rule for substitution at line 2).
Thanks to Dennis for finding bug on double quote (and providing solution)
Zsh + GNU coreutils — 48 bytes (1 space)
for x (**/*(Dod))mv -T --b=t $x $x:h/${${x:t}// }
It's weird that you hate (ASCII) spaces but are fine with tabs and newlines, but I guess it takes all kinds.
zmv solves a lot of file renaming problems concisely (and only slightly obscurely). However, it insists on the targets being unique; while you can easily add unique suffixes, adding a suffix only if it would be needed pretty much requires re-doing all the work. So instead I loop manually and rely on GNU mv to append a unique identifier in case of collision (--backup
option, plus --no-target-directory
in case a target is an existing directory, as otherwise mv
would move the source inside that directory).
(od)
is a glob qualifier to sort the output with directories appearing after their content (like find's -depth
). D
includes dot files in the glob. :h
and :t
are history modifiers similar to dirname
and basename
.
mv
complains that it's called to rename files to themselves, because the glob includes file names without spaces. C'est la vie.
Ungolfed version:
for x in **/*\ *(Dod); do
mv --no-target-directory --backup=numbered $x ${x:h}/${${x:t}// /}
done
Python 180 bytes
from os import*
t,c,h='.',chdir,path
def g(p):
c(p)
for x in listdir(t):
if h.isdir(x):g(x)
n=x.replace(' ','')
while h.exists(n):n+=t
if' 'in x:rename(x,n)
c(t*2)
g(t)
only 2 spaces if you use tab for indentation :-)