I HATE spaces in file names

61

19

It is simple. I cannot stand when people use spaces when naming files. It sometimes wrecks console commands and makes the output of ls ugly.

The challenge is to write a program (only ascii characters) which

  1. renames all files (including directories) in the current directory to versions with spaces removed or replaced by '_'
  2. on collision, you need to append a unique identifier (up to you)
  3. descends recursively into all subdirectories

You can assume UNIX-style path names. Who would need this program on a Windows machine anyways?

This is code golf, the shortest program wins (#ascii characters). Since I hate spaces so much, each space has to be counted twice.

Please provide your language, score, program and a short description of how to run it.

The program must compile and execute with reasonable effort on my linux machine.

EDIT: As Etan requested a file structure for testing, here is the script I currently use to create a suitable file tree:

#!/bin/bash
rm -r TestDir

touchfiles()
{
    touch my_file
    touch my__file
    touch "my file"
    touch "my  file"
    touch " my_file  "
}

mkdir TestDir
cd TestDir

touchfiles

for dir in "Test Sub" Test_Sub "Te stSub" Te_stSub
do
    mkdir "$dir"
    cd "$dir"
    touchfiles
    cd ..
done

M.Herzkamp

Posted 2014-08-06T12:03:42.803

Reputation: 1 227

It's tempting to write an answer in batch.. ;) – globby – 2015-01-21T15:52:00.680

renames all files in the current directory to versions with spaces removed – Дамян Станчев – 2014-08-06T12:17:56.883

I will edit the challenge to allow for replacements. – M.Herzkamp – 2014-08-06T12:21:55.853

@ДамянСтанчев I know, but the first submission used a replacement and the OP didn't complain. ;) – Martin Ender – 2014-08-06T12:28:00.623

1How to replace one char with another in all filenames of the current directories? – The Guy with The Hat – 2014-08-06T14:02:22.367

22This is begging for a solution made without ascii chars. – Dennis Jaheruddin – 2014-08-06T15:44:33.033

Well, are you running TeX on a Windows machine? That will leave you viscerally hating that space you left in the name of your images directory. – E.P. – 2014-08-06T16:27:10.407

50Now I want to learn Whitespace – BrunoJ – 2014-08-06T18:03:45.633

2Is it okay to just replace all spaces in filenames on the system with random garbage from /dev/urandom? – Nit – 2014-08-06T18:27:48.727

You can remove spaces or replace them with '_' – M.Herzkamp – 2014-08-06T20:43:27.100

@Dennis Edited the challenge to fill that loophole – M.Herzkamp – 2014-08-06T20:56:05.623

Are "Tabs" (\t) considered spaces for scoring? – Kevin Fegan – 2014-08-07T00:51:11.547

In case of name collision, should "unique identifier" be appended to the file "name", and the file "extension" left alone? Like: index 1.html -to- index1-02.html, not index1.html-02. – Kevin Fegan – 2014-08-07T00:57:08.603

It might be wise to add an extended example. 'Collisions' get confusing with a deep tree where multiple subdirs have spaces. – Michael Easter – 2014-08-07T02:16:31.897

10@BrunoJ doing this in Whitespace would first require you to develop a file access system in WS. I think that would be more challenging than the actual challenge. – Nzall – 2014-08-07T06:40:07.430

1"Who would need this program on a Windows machine anyways?" - people calling "Program files" content in console ;) Its same issue as on Unix, and Windows for some time accepts paths with / as well. – PTwr – 2014-08-07T07:28:56.533

@Kevin Append the identifier to the extension. I don't hate tabs as much, so they only count once. – M.Herzkamp – 2014-08-07T09:35:16.637

1@PTwr "Windows for some time accepts paths with / as well" — since always. And MS-DOS too (since directories, to be accurate). – Athari – 2014-08-07T12:04:53.370

@Athari thanks for clarifying, I was not sure if non-NT supported it. – PTwr – 2014-08-07T13:04:42.133

7Waiting for someone to post a C/C++ solution so I can steal it, compile, post in hex as x86 machine code with ZERO spaces! [or maybe base64] – Mark K Cowan – 2014-08-07T13:35:48.353

10I hate underscores in filenames. Use dashes. – HostileFork says dont trust SE – 2014-08-07T17:07:56.217

Is there a corpus of files to test solutions against? – Etan Reisner – 2014-08-08T01:27:07.513

@Dr. Rebmu: Why? – M.Herzkamp – 2014-08-08T08:54:09.297

@Etan: included my test tree creator script – M.Herzkamp – 2014-08-08T08:54:34.837

When collisions are encountered, do we have to append to the filename that had spaces or can we append to the original file as well? – Dennis – 2014-08-08T13:50:45.347

@MarkKCowan you don't need to compile and use hex for that: all spaces outside strings can be substituted with newlines, and all spaces inside strings can be substituted with the corresponding ASCII code (32) – pqnet – 2014-08-08T14:17:31.897

1"I cannot stand when people use spaces when naming files. It sometimes wrecks console commands." ... man I hate this nail in my toolbox, it destroys my hammer! – user541686 – 2014-08-08T19:25:58.363

2You should include some characters that cause problems in badly-written shell scripts in your test harness: backslash, newline, tabs, *, … – Gilles 'SO- stop being evil' – 2014-08-08T21:32:42.587

1

@M.Herzkamp Don't have to hit shift to type them, considered a word separator in RegEx/Unicode, doesn't sink down and disappear into a bounding box if the filename winds up in a box, less awkward typographically if you ever have to communicate about a filename as a URL in print, there are probably other reasons...

– HostileFork says dont trust SE – 2014-08-09T07:57:28.633

@pqnet Compiled-to-hex asm would use less chars when posted here :) – Mark K Cowan – 2014-08-09T15:30:21.230

1Please include non English Characters as a test case. – ojblass – 2014-08-13T20:32:26.877

Answers

10

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

Gilles 'SO- stop being evil'

Posted 2014-08-06T12:03:42.803

Reputation: 2 531

1this does not rename my files at all! – M.Herzkamp – 2014-08-11T11:56:21.317

@M.Herzkamp Oh, right, zmv bombs out before mv has a chance to sort out collisions. Ok, I'm doing this manually. Turns out to be exactly the same length if I skip dot files and even saves a character if I don't. – Gilles 'SO- stop being evil' – 2014-08-11T16:15:20.607

1Now it's working. Btw: I included the space penalty at a time where I really had a grudge against spaces ;) Ironically, I did not exclude spaces when I posted the challenge :P – M.Herzkamp – 2014-08-12T07:36:26.167

13

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)

pqnet

Posted 2014-08-06T12:03:42.803

Reputation: 239

11IS THE EXTRA SPACE BEHIND find THERE TO MOCK ME??? ;-) – M.Herzkamp – 2014-08-06T22:01:57.823

@M.Herzkamp I though it was a copy and paste error, but it is actually there. Guess I gained 2 more points. Also, -depth in GNU can be replaced by -d, though it complains that it is deprecated. I don't know about the rules of golf, can I do that? – pqnet – 2014-08-06T22:16:02.393

2As long as it works, I allow it. Should the deprecation become removal in a future version though, I might have to come back to this answer and downvote it for not being correct ;-) – M.Herzkamp – 2014-08-06T22:21:07.683

removed the extra space and added the non-posix version – pqnet – 2014-08-06T22:30:30.933

Wow, I like it. Nice way to work around the problems, +1 – german_guy – 2014-08-07T06:51:59.490

@german_guy you mean about the trick of adding dots to the name until there is a name which is not present? In a previous version I used mktemp to generate a non-existing file with random numbers at the end, which I liked more, but I switched to this because it is a bit shorter – pqnet – 2014-08-07T10:06:24.317

2This won't work properly if any of the filenames contains a double quote. To fix this, you can use bash -c 'B=${0##*/}...' {} \; instead, which is actually shorter. – Dennis – 2014-08-07T14:53:07.303

@Dennis makes sense. Now that I have a better look at it, it doesn't work if the file name starts with one or two dashes. I'll try to fix both issues then edit again – pqnet – 2014-08-08T09:34:35.440

Are you sure? If find is called without a path, {} will always begin with ./. – Dennis – 2014-08-08T12:42:37.797

@Dennis oh right it makes sense, no need to use -- – pqnet – 2014-08-08T13:43:02.400

3I guess I will be that guy, what is up with N variable? It is never defined... – Steven Penny – 2014-08-09T05:04:24.993

@StevenPenny error with edit, used to be $N but I removed the variable to gain more space. Should be $0 now. Thank you for noticing, I fixed it. – pqnet – 2014-08-12T00:36:53.667

9

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 :-)

Emanuele Paolini

Posted 2014-08-06T12:03:42.803

Reputation: 191

I guess most of the other answers could improve their score by using tabs instead of spaces as well. – kasperd – 2014-08-07T08:14:33.437

But your submission uses spaces doesn't it? (+1 for working code) – M.Herzkamp – 2014-08-07T09:40:53.963

I don't know how to att tab characters in the answer... – Emanuele Paolini – 2014-08-07T10:11:21.383

You can copy-paste them in, like I just did. The Markdown parser, alas, converts them to spaces, but you can always get the original code with tabs by clicking "edit". – Ilmari Karonen – 2014-08-07T10:26:42.747

There are still more than two spaces (e.g. in for statement) – M.Herzkamp – 2014-08-07T11:00:56.993

2replaced with tabs :-) – Emanuele Paolini – 2014-08-07T11:14:07.697

3How ugly... Well, I guess I asked for it :( – M.Herzkamp – 2014-08-07T11:33:10.717

You can remove all spaces by adding ,s=chr(32) at the top and replacing all instances of ' ' with s. That only increases the length to 188 bytes. – Pluto – 2014-08-08T19:18:29.180

@Pluto: you gain 2 point and loose 8... so it is not convenient. – Emanuele Paolini – 2014-08-08T19:32:28.113

5

If the order of collided file suffixes does not need to give precedent to the pre-existing file then the following works for me:

bash/find/mv 84 bytes, 16 spaces

find -depth -execdir bash -c '[ "${0//[^ ]}" ] && mv -{T,--b=t} "$0" "${0// }"' {} \;

bash/find/mv 82 bytes, 14 spaces

find -depth -execdir bash -c '[ "${0//[^ ]}" ]&&mv -{T,-b=t} "$0" "${0// }"' {} \;

Cuddled && to save two space bytes.

bash/find/mv 60 bytes, 11 spaces

find -d -execdir bash -c 'mv -{T,-b=t} "$0" "${0// }"' {} \;

Drops error protection so it gets errors from mv on files which have no spaces to start with.

Edit: Dropped the quotes from {} as reminded by Dennis. Also allowed find to scream about portability and deprecation in the shortest version where mv is already screaming about moving a file on top of itself.

Edit 2: Added -T to mv command to avoid nesting directories instead of renaming as pointed out by pqnet. Used brace expansion at cost of one character over just using one space.

Etan Reisner

Posted 2014-08-06T12:03:42.803

Reputation: 151

You can use -d instead of -depth and you don't need the quotes around {}. – Dennis – 2014-08-08T19:21:27.783

@Dennis Yeah. I saw the -d conversation on pqnet's answer but figured since I was silencing the mv screaming I'd avoid the find screaming. Though I should probably shorten it for the screaming one. And yeah, I always quote {} for some reason even though I know you don't have to in this case. Force of habit I guess. – Etan Reisner – 2014-08-08T19:30:43.797

1When collision happen on directory names, it will put one into another (and not strip spaces). Use -T option to mv to avoid this – pqnet – 2014-08-08T22:53:52.220

This works, and I said in the challenge that the appendix is up to you. +1 – M.Herzkamp – 2014-08-12T07:48:03.823

4

NodeJS – 209 bytes, 3 Whitespaces

s=require('fs');function a(d){s.readdirSync(d).forEach(function(f){f=d+'/'+f;i=0;r=f;if(/ /.test(f)){r=f.replace(' ','');while(s.existsSync(r))r+=i++;s.renameSync(f,r)}s.statSync(r).isDirectory()&&a(r)})}a('.');

c.P.u1

Posted 2014-08-06T12:03:42.803

Reputation: 1 049

I am not familiar with node.js. How would I run it? – M.Herzkamp – 2014-08-07T12:22:36.027

You'll need the Node executable nodejs; save it in a file and run node file.js

– c.P.u1 – 2014-08-07T12:27:07.200

7I get an exception TypeError: Object #<Object> has no method 'exists'. Guess where: it's in line 1! :D – M.Herzkamp – 2014-08-07T12:45:51.493

I have tested it. Anyway, I replaced exists with its synchronous counterpart. Can you try now? – c.P.u1 – 2014-08-07T12:53:42.953

Still the same exception. Somehow s is not initialised properly. Is there any package I need to run it? Also, I believe you want to replace spaces with numbers. However, the challenge states to either remove them or to replace them with an underscore. – M.Herzkamp – 2014-08-07T13:01:45.457

Well, I'm running Node v0.11.12 on OS X and it works. As to the second part, I have edited my post to reflect that. BTW, If s weren't initialised, the first call to s.readdirSync would've thrown an exception. Also s is assigned only once. – c.P.u1 – 2014-08-07T13:12:39.793

1I have only version 0.6.12 installed. That may be the problem. – M.Herzkamp – 2014-08-07T13:26:36.923

You can probably remove the Synchs again, and the i=0; seems superfluous now. – M.Herzkamp – 2014-08-07T13:31:04.127

Just now compiled v0.10.30, and it works like a charm. +1 – M.Herzkamp – 2014-08-07T13:42:45.533

This solution does not handle multiple spaces within a file/directory name. Here is a version which fixes that issue and is optimized to 190 bytes with no whitespaces: http://pastie.org/9456328

– nderscore – 2014-08-08T18:27:23.603

2

PHP, 147 145 bytes, 2 1 spaces -> 146

function    s(){foreach(glob("*")as$n){is_dir($n)&&chdir($n)&s()|chdir("..");if($n<$r=strtr($n," ",_)){while(file_exists($r))$r.=_;rename($n,$r);}}}

recursive function. Run with s(".");

Loop through glob results for given path:

  • if directory, recurse
  • replace spaces with underscore
  • if strings differ
    • while new filename is taken, append underscore
    • rename file/directory

Titus

Posted 2014-08-06T12:03:42.803

Reputation: 13 814

php will rename the files on the server... Now I wonder how to change the file names of a client whenever they visit your site :D – M.Herzkamp – 2017-02-06T11:22:36.273

2

Bash + Perl rename 64

(rename is the Perl script on Debian and derivatives, not the util-linux command.)

find . -depth -name "* *" -execdir rename 'y/ /_/' * \;

german_guy

Posted 2014-08-06T12:03:42.803

Reputation: 569

11What happens if "my file.txt" and "my_file.txt" are both present? – M.Herzkamp – 2014-08-06T12:15:30.163

1Oh true.. Working on that soon – german_guy – 2014-08-06T12:16:09.223

1* should be {}, as it stands this only renames files whose name appears in the current directory. This doesn't append a suffix in case of collision. You could save quite a bit by omitting -name "* *" since rename silently ignores files whose name is not transformed. – Gilles 'SO- stop being evil' – 2014-08-08T21:28:26.007

2

Bash - 86 bytes

find    .   -d|while    IFS=""  read    f;do    t=${f##*/};mv   --b=t   -T  "$f"    "${f%/*}"/${t// /};done

Subbeh

Posted 2014-08-06T12:03:42.803

Reputation: 184

Oops, will have a look – Subbeh – 2014-08-06T13:04:49.903

2Also, spaces are counted twice ;-) – M.Herzkamp – 2014-08-06T13:07:25.570

what exactly do you mean with spaces are counted twice? – Subbeh – 2014-08-06T13:14:07.580

@Subbeh a space counts as two bytes rather than one – None – 2014-08-06T13:17:06.320

1You can save a lot of characters by abbreviating --backup to --b – None – 2014-08-06T13:17:58.390

if there is directory collision for some reason all files are put into the same directory. Also spaces are stripped out instead of being replaced by underscores (i guess you should use ${f// /_} ) – pqnet – 2014-08-06T21:37:18.800

It is ok to remove them. As long as the possible collisions are handled properly. – M.Herzkamp – 2014-08-06T22:04:30.250

You get the following kind of error repeatedly: mv: cannot stat \./Test_Sub/ my_file': No such file or directory` Also, you have the problem, that you delete the spaces of parent directories in path names. – M.Herzkamp – 2014-08-06T22:27:02.990

I changed the code to work better with parent directories. I don't see any problems with the testset I'm using now. – Subbeh – 2014-08-07T11:11:31.810

Your solution somehow has problems with files ending in spaces. I guess the read trims them? – M.Herzkamp – 2014-08-07T11:42:44.993

Alright, seems like it's fixed now -_- – Subbeh – 2014-08-07T12:10:30.000

1Yes, now it works with my test set as well! +1 – M.Herzkamp – 2014-08-07T12:47:47.937

while IFS="" read is missing -r after read to cope with backslashes. OTOH you can omit the "" after IFS=. See How to loop over the lines of a file? In the mv command, "${f%/*}"/${t// /} should be "${f%/*}/${t// }" (quote properly, and save 1 character). – Gilles 'SO- stop being evil' – 2014-08-08T21:36:22.110

@Etan: What exactly does not seem to work? I don't see any problems with the result. – M.Herzkamp – 2014-08-11T11:30:20.423

You could use newlines intead of ;, will make your code more readable without increasing the number of bytes – pqnet – 2014-08-12T00:39:03.320

Sorry, this does appear to work. I had a mv alias getting in the way it seems. But I guess that's a semi-valid caveat. – Etan Reisner – 2014-08-12T17:17:41.880

2

POSIX sh + GNU find + GNU mv 67 ASCII bytes + one (literal) space

find    -d  -exec   sh  -cf 'IFS=\ ;IFS=_   set $0;mv   --b=t   "$0"    "$*"'   {}  \;

I don't know if it fits, but with this any sequence of spaces is elided to a single _ - I like it anyway. Actually any sequence but leading/trailing spaces that is - those are automatically truncated (which is also, I think, a beneficial behavior). Thanks to Gilles for pointing this out.

This just uses the internal field separator to separate fields.

It's fairly... chatty...

...oh man. I knew the tab thing was cheap, but I thought it was at least clever. Now I'm just late to the party...

mikeserv

Posted 2014-08-06T12:03:42.803

Reputation: 181

This works on my test set as you intended, but not as the challenge requires. I like it, though, because I will probably learn something new. I guess I will have to read up on this IFS magic thingy... – M.Herzkamp – 2014-08-11T11:44:36.627

1

@M.Herzkamp - ifs behaves differently depending on whether it is set to whitespace or not. Most people hate it because they do not understand its two primary qualities - that it only operates on expansions ($expand not (ex pand)) and the ifsws thing just mentioned. Look here

– mikeserv – 2014-08-11T12:08:44.990

This doesn't rename files inside directories whose names contain spaces. A fix would be to replace -exec with -execdir. Ånother quirk of IFS that you aren't mentioning is that trailing spaces are deleted. Note that as others have noticed you need the -T option to mv as well, for when the target of an mv call is an existing directory. – Gilles 'SO- stop being evil' – 2014-08-11T16:26:57.210

@Gilles - my preference would be to use sh -c 'mkdir -p ../newtree/"$0"; ln "$0"/* ../newtree/$0 {} \; and other globs on a find -type d command to create a mirrored tree of hardlinks and then to operate on those, but I'm second-guessing writing a code-golf at all for a move operation. Good point about the leading/trailing spaces, though I think that is also a behavior I would prefer. – mikeserv – 2014-08-11T17:50:41.237

@Gilles - but by the way, it is not a quirk - it is an intended and standards-controlled behavior. The Field-Splitting section is among the very few in the shell spec that does not contain the words unspecified or implementation-defined. There are no such guarantees with zsh's builtin function zmv for instance.

– mikeserv – 2014-08-12T05:45:01.007

On further reflection, my earlier judgement seems to be unjustified. The challenge says that you can either remove spaces or replace them with an underscore. However, it does not mention that it has to be done consistently. If you chose to replace some spaces and remove others, it seems to be a valid solution. +1 – M.Herzkamp – 2014-08-12T07:50:46.270

@M.Herzkamp perhaps valid, but still crazy. Dont golf your fs - there are too many sandtraps. – mikeserv – 2014-08-12T07:57:03.953

1

Ruby 121

require 'find'

Find.find('.') do |file|
  if file.chomp.match(/ /)
    File.rename(file, file.gsub(/ /, '_'))
  end
end

gam3

Posted 2014-08-06T12:03:42.803

Reputation: 111

6

Welcome to Code Golf! The idea here in these [tag:code-golf] challenges is to use the fewest number of characters. That means that you can definitely get rid of blank lines and tabs, and make variable names single-character, but people look for all sorts of creative ways to reduce character count.

– Not that Charles – 2014-08-06T20:23:27.220

I get an error, that the Directory is not empty:gam3.rb:5:in \rename': Directory not empty - ./Te stSub or ./Te_stSub (Errno::ENOTEMPTY) from gam3.rb:5 from /usr/lib/ruby/1.8/find.rb:39:in `find' from /usr/lib/ruby/1.8/find.rb:38:in `catch' from /usr/lib/ruby/1.8/find.rb:38:in `find' from gam3.rb:3` – M.Herzkamp – 2014-08-06T21:04:05.393

1

Python, 187

165, plus 22 penalty points for the spaces.

from os import*
u='_';j=path.join
for t,d,f in walk('.',0):
 for z in f+d:
  n=z.replace(' ',u)
  if n!=z:
   while path.exists(j(t,n)):n+=u
   rename(j(t,z),j(t,n))

166, using Emanuele's \t trick:

Only a single space in this one!

from    os  import*
u='_';j=path.join
for t,d,f   in  walk('.',0):
    for z   in  f+d:
        n=z.replace(' ',u)
        if  n!=z:
            while   path.exists(j(t,n)):n+=u
            rename(j(t,z),j(t,n))

Henry Keiter

Posted 2014-08-06T12:03:42.803

Reputation: 131

This works for me. +1 – M.Herzkamp – 2014-08-08T09:08:35.467

remove the spaces at the beginning of the lines and use tabs - they are no spaces so count only once – chill0r – 2014-08-13T15:13:27.737

@chill0r That's what the second version is; all the spaces are replaced with tabs except one (except SO still displays them as spaces). – Henry Keiter – 2014-08-13T15:33:17.453

1

LiveScript - 166

(Replace spaces with tabs.)

(a=->(s=require \fs)readdirSync(it)map (f)->f=it+'/'+f;r=f.replace /\s/g,i='';(while f!=r&&s.existsSync r=>r+=i++);s.statSync(f)isDirectory(s.renameSync f,r)&&a r) \.

Based on nderscore's optimized version of c.P.u1's answer.

nyuszika7h

Posted 2014-08-06T12:03:42.803

Reputation: 1 624

Works! +1 I am going to delete my comments earlier to tidy this post. – M.Herzkamp – 2014-08-14T11:42:36.300

0

POSIX(Tested on zsh) + basic Linux commands 151

export IFS='
'
for f in $(ls -R1);do export n=$(echo $f|tr ' ' '_');yes n|mv $f $n || yes n|mv $f `echo $n;echo $f|md5sum`
done

LinGeek

Posted 2014-08-06T12:03:42.803

Reputation: 11

@M.Herzkamp Fixed. – LinGeek – 2014-08-06T13:14:19.953

Several things: what is the function of export IFS and the c in ls -cR? And what version of mv do you need for --reply option? (I have 8.13, and it does not recognise the option). Also to get a better score, you should abbreviate your variable names. – M.Herzkamp – 2014-08-06T13:34:45.457

The c replaces spaces with newlines. The IFS stops spaces being separators. The --reply is from old versions and is about to be fixed. – LinGeek – 2014-08-06T13:49:18.647

@M.Herzkamp Fixed. – LinGeek – 2014-08-06T13:53:11.590

1Are you missing a second mv in line 5? And I think one echo in that line is wrong. – M.Herzkamp – 2014-08-06T20:58:45.953

1$(ls -CR) is completely bogus. The -c option is useless, and -R gets you files without their directory, which is pointless. Your architecture fundamentally won't handle file names containing newlines. You need set -f or else file names containing wildcards will explode. export is useless. I can vaguely see what you're trying to do to uniquify files, but the piping is wrong. – Gilles 'SO- stop being evil' – 2014-08-08T21:31:35.960

@Gilles This works, because of the file structure in the challenge – LinGeek – 2014-08-14T16:12:12.340

The challenge is not limited to the tests given in the question, they're just that: tests. But your code (neither the original nor the updated) doesn't even work on these test cases (neither in bash nor in zsh)! – Gilles 'SO- stop being evil' – 2014-08-14T16:20:14.560

0

Bash 4+ 111 bytes

shopt -s dotglob globstar
for f in **
do
n=${f// /}
while [[ $f != $n && -e $n ]]
do n+=1
done
mv "$f" $n
done

user30335

Posted 2014-08-06T12:03:42.803

Reputation:

1Same problems as several other entries: You replace spaces in parent directories and mv cannot find them. Also you must change direction of traversion, otherwise you rename directories and mv cannot find the files inside. – M.Herzkamp – 2014-08-08T08:59:16.513

0

Groovy, 139 characters

def c
c={
f->
def g=new File(f.parent,f.name.replaceAll('\\s',''))
f.renameTo(g)
!g.directory ?: g.eachFile(c)
}
new File('.').eachFile(c)

according to @edc65 comment

Groovy, handle collisions, 259 characters

def c
c={
p,l,f->
def g=new File(p,f.name.replaceAll('\\s',''))
f==g?:
(g.exists()?f.renameTo(g.toString()+l.indexOf(f.name)):f.renameTo(g))
!g.directory?:g.eachFile(c.curry(g,g.list().toList()))
}
def r=new File('.')
r.eachFile(c.curry(r,r.list().toList()))

login

Posted 2014-08-06T12:03:42.803

Reputation: 111

1This does not handle collisions. – edc65 – 2014-08-13T13:00:04.793

Make sure that files are renamed before their parent directories are, and that the spaces in parent directories are not replaced. – M.Herzkamp – 2014-08-14T11:49:55.363

I'm sure it's ok – login – 2014-08-14T15:36:44.417