I need some randomness
June 27, 2010 7:50 AM   Subscribe

I am looking for a program or batch file to pick a random file from a folder and all of it's sub- (and sub-sub-) folders. The entire tree. Said program/batch is proving difficult to find on the internet.

I have a large archive of downloaded html files saved on my hard drive, which I re-read for fun (they're fiction). However, I do tend to gravitate toward the same ones. I'd like something to randomly serve me up one file anytime I ask, from anywhere in the tree.

I found one or two programs out there, but they either don't work at all ("there are no files in that directory" - uh, yes there are), or they only work on a single folder, not its subfolders. I tried a batch file after changing the parameters, but it didn't work, and I can't figure out why.

I don't care if I'm just given a filename to go find myself, a link, or a copy of the actual file. I just want to widen my reading habits. I would like like to be able to specify more than one extension, as a minority of the files are actually txt, rtf, and mht.

This is a Windows system - WinXP. I don't program, so I don't have anything installed that could run a Perl script or anything like that. Although I guess I'd be willing to install something like that if it were the best option.
posted by timepiece to Computers & Internet (20 answers total) 1 user marked this as a favorite
 
The bottom of this page has something that sounds awfully similar. Basically, .bat files that make a file with a list of files, then pulling a random path, and opening it. I think, I don't have Windows running so can't test it. But that sounds likely given what little I know about Windows shell scripting.
posted by artlung at 8:13 AM on June 27, 2010


Python two-liner:

import random, os, sys, itertools
print random.choice(list(itertools.chain(*[[os.path.join(dir,file) for file in files] for (dir,dirs,files) in os.walk(sys.argv[1])])))


Usage:
0. install python
1. put in file named random.py
2. python.exe random.py directory
posted by qxntpqbbbqxl at 8:20 AM on June 27, 2010


Response by poster: Usage:
0. install python
1. put in file named random.py
2. python.exe random.py directory


I assume 2 is meant to go in the Run box? It doesn't seem to do anything. A window appears and disappears too quick to see what it is, no file is opened. Random.py is saved to the Python directory.
posted by timepiece at 9:11 AM on June 27, 2010


put "cmd" in the run box to get a command prompt, then type #2
posted by qxntpqbbbqxl at 9:34 AM on June 27, 2010


in your run box, type in 'cmd', go to your python installation directory with 'cd C:\Python26', then type in step 2 at the prompt, and you should be good.
posted by Mach5 at 9:35 AM on June 27, 2010


No, I think you're supposed to use it from the interactive Python prompt - otherwise, that window will just print the filepath and then close.
posted by McBearclaw at 9:38 AM on June 27, 2010


Agh, preview.
posted by McBearclaw at 9:39 AM on June 27, 2010


Perl FTW!


#!/usr/bin/perl
use File::Find;
File::Find::find(sub{f$_&&/\.html\z/i&&(++$i)&&rand()<(1/$i)&&($f=$File::Find::name)},$ARGV[0]);print"$f\n";exec('google-chrome', $f);

If you have a single directory that you are intending to search under all the time they you can get rid of the $ARGV[0] and replace it with the directory (e.g. "C:\Path\to\My Documents". I'm not really sure how stuff works on windows. The magic part here is the exec('google-chrome',$f) part, that will open in google-chrome browser. I believe on windows you can use 'start' or 'start.exe' to automagically open a file in the browser. I do believe that if you put the above in a .pl file, and changed the ARGV[0] to a specific directory and fix the browser/start thing that you could just double click it and poof a random file would show up in your browser.

The same probably works for the Python, the magic part would be:

os.system("start ",random.choice(.......) )

That's removing the 'print' and wrapping the 'random.choice()' in a os.system call, and you would change the sys.argv[1] to your directory name. Then you could just double click it.
posted by zengargoyle at 9:52 AM on June 27, 2010


Best answer: setLocal EnableDelayedExpansion

MD Temp

FOR /F "tokens=* delims= " %%A in ('dir "YOURDIRECTORYHERE" /s/b') do (
echo %%A >> "Temp\%%~nA.txt" )

for /f "tokens=1 delims=" %%T in ('dir "Temp"/s/b *.txt') do (
ren "%%T" "!random!%%~nT.txt" )

COPY "Temp\*.txt" Randomized.txt

RD /s /q Temp


Copy that into a text file and rename it with a .bat extension. Replace YOURDIRECTORYHERE with the relevant path, such as "C:\Junk\HTML Stories" or whatever.

This will look at all the files in all the folders in that directory and generate a text file with every file in that directory listed in a random order. It creates a temp folder and it may take a minute to run if you have a lot of files, but it should do what you need with no need to install anything special (assuming a Windows environment).
posted by Menthol at 2:26 PM on June 27, 2010


Response by poster: Beautiful, Menthol. Just what I wanted. So glad it was doable with Windows tools, and I can uninstall stuff I'm not going to use for anything else.

Does anyone know if there's a way to truncate it? It does take it a while to run 1993 files.
posted by timepiece at 6:14 PM on June 27, 2010


Best answer: Don't settle for a kludge. The next time you want to do something unique, you'll still be left with slow kludgey batch files that barely do what you want. Install a real scripting language.
import random, os, sys, itertools, re

docdir = r'Z:\usr\share\doc'

docs = list(itertools.chain(*[[os.path.join(dir,file) for file in files] for (dir,dirs,files) in os.walk(docdir)]))

match = re.compile(".*\.html?$", re.IGNORECASE)
docs = [f for f in docs if match.match(f)]

os.system("start "+random.choice(docs))
Put that in a Random.py file.
Change Z:\usr\share\doc to the path to your documents.
If you don't want to limit the files to .html or .htm files take out the 'match' line and the 'match.match' line.
Double click the file, presto! random file opened in browser.

This way you get what you wanted, it's way faster, and the next time you have special needs you'll already be prepared for another simpe Python solution.

This even works under Wine on Linux with Python 2.6

I still prefer Perl :)
posted by zengargoyle at 12:16 AM on June 28, 2010


Oh, to get rid of the black console window that pops up...
> In Windows, I have been simply using os.system() to run command line
> program in python. but there will be a black console window. How can I
> run the program without invoking that window? i guess there are some
> function with which I can redirect the output?

name your scripts with .pyw extensions instead of .py extensions
posted by zengargoyle at 12:34 AM on June 28, 2010


Response by poster: I understand what you're saying zengargoyle, but this is the first time I've needed a batch file/unique solution for several years - I'm willing to put up with a kludge rather than trying to learn an entirely new programming language. Especially since that would constitute my FIRST programming language.

Although, I do now feel the need to go home, reinstall Python, and try your version of the script. Just to see.
posted by timepiece at 8:33 AM on June 28, 2010


Response by poster: OK zengargoyle, I tried the new Python script, and it does work pretty well and much quicker than the batch file. However, I am getting an error message every time the resulting file name has spaces in it (the majority). Any way to make it stop choking on those? Or would I have to rename all my files?

It's not an insurmountable problem, since the error message still provides me with enough of a file name to open it myself, most times. But the fully automated way is nicer.
posted by timepiece at 5:21 PM on June 28, 2010


I was afraid of that, I was going to complain that Python didn't have a os.exec([]) call. Turns out it's in the 'subprocess' module.

Try this, change the 'import' line to:
import random, os, sys, itertools, re, subprocess
And change the 'os.system' line to:
subprocess.call(["start",random.choice(docs)])
This should do it from what I gather with a quick google, but this is at the point that I can't really test under Wine (which does some fancy under-the-hood stuff).

Once it works to your liking, you may want to expand it to work with .pdf and .doc files for example..
Change:
match = re.compile(".*\.html?$", re.IGNORECASE)
To:
match = re.compile(r'\.(?:html?|pdf|doc)\z', re.IGNORECASE)
(Which is how I should have done it in the first place. :) )
posted by zengargoyle at 10:15 PM on June 28, 2010


Response by poster: No, the subprocess.call line seems to break it. Just a blank black window that disappears without opening any files or giving any error message. The import line did not change anything that I noticed.

And I'm not using the match lines at all - there's nothing in this directory tree I wouldn't want, I don't need to specify file types. (just so you can see how I arrange things, the directory is E:\downloads\stories\)
posted by timepiece at 6:59 PM on June 29, 2010


This is me on Windows
You know what? I installed XP on a virtual machine, and during my stumbling around Windows ended up with two randomdoc files on my desktop, one .py and one .pyw, and ended up looking at the wrong one.

You need the import line just to tell Python that you're going to use the 'subprocess' module in your program, just like the 'random', 'os', ... you can actually get rid of the 're' and 'sys' now that they're not used.

This is what I have in a file on my desktop called 'randomdoc.pyw', it does work (honest):
import random, os, itertools, subprocess
docdir = r'C:\Foo'
docs = list(itertools.chain(*[[os.path.join(dir,file) for file in files] for (dir,dirs,files) in os.walk(docdir)]))
f = random.choice(docs)
subprocess.call(['explorer',f])
Only issue is for .doc and .rtf files it asks you if you want to download them or open them. Click Open and they pop up in WordPad. Tested with filenames/directories with spaces for filetypes .html, .txt (opens browser), .doc, .rtf (asks then opens wordpad), .bmp (opens image editor)

If anybody else is watching... Evidently 'start' is a CMD.EXE shell builtin on real Windows, but emulated by a program file 'start.exe' under Wine. I never could figure out the magic to call cmd.exe without having it leave a open blank window. Windows is cranky.
posted by zengargoyle at 10:55 PM on June 29, 2010


Response by poster: First, I really appreciate all your help. And the correct version does work perfectly.

Except the files open in IE (ugh!). I'm assuming that has something to do with the
subprocess.call(['explorer',f])
Any way to force Firefox? I tried changing it to
subprocess.call(['firefox',f])
but that did not work.
posted by timepiece at 4:28 PM on June 30, 2010


You just need the full path to firefox (since it doesn't live in the default path).
subprocess.call(r'C:\Program Files\Mozilla Firefox\firefox.exe', f)
If you need command line options, they'll go like this:
subprocess.call(r'C:\Program Files\Mozilla Firefox\firefox.exe', '-no-remote', '-P', 'John Smith' ,f)
Which would open a new browser (instead of a tab) using the 'John Smith' profile instead of the default user profile. The "r" in front of some of the ' just keep Python from mucking up the "\" characters in the path strings.
posted by zengargoyle at 2:29 AM on July 1, 2010


Response by poster: Perfect.

I had to mess around with it a little, as copying the line above didn't quite work. It wanted those square brackets that were in the first version, evidently. I ended up with:
subprocess.call([r'C:\Program Files\Mozilla Firefox\firefox.exe',f])
Thank you so much for devoting all this time to my little script. You can rest assured that I'll probably use it almost daily.
posted by timepiece at 4:40 PM on July 1, 2010


« Older Is "crea spatium" proper Latin?   |   How can I help my high-strung dog transition to a... Newer »
This thread is closed to new comments.