views:

258

answers:

5

Hello,

I would like to generate a random filename in unix shell (say tcshell). The filename should consist of random 32 hex letters, e.g.:

c7fdfc8f409c548a10a0a89a791417c5

(to which I will add whatever is neccesary). The point is being able to do it only in shell without resorting to a program.

+3  A: 

Tested in zsh, should work with any BASH compatible shell!

#!/bin/zsh

SUM=`md5sum <<EOF
$RANDOM
EOF`

FN=`echo $SUM | awk '// { print $1 }'`

echo "Your new filename: $FN"

Example:

$ zsh ranhash.sh
Your new filename: 2485938240bf200c26bb356bbbb0fa32
$ zsh ranhash.sh
Your new filename: ad25cb21bea35eba879bf3fc12581cc9
LukeN
I thought this was good too :)
fmark
+5  A: 

Assuming you are on a linux, the following should work:

cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32

This is only pseudo-random if your system runs low on entropy, but is (on linux) guaranteed to terminate. If you require genuinely random data, cat /dev/random instead of /dev/urandom. This change will make your code block until enough entropy is available to produce truly random output, so it might slow down your code. For most uses, the output of /dev/urandom is sufficiently random.

If you on OS X or another BSD, you need to modify it to the following:

cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-f0-9' | head -c 32
fmark
Dang, and I thought mine was good :)
LukeN
@LukeN: Yours was good. And technically, this solution is not guaranteed to terminate :)
GregS
This solution was actually doing weird things for me, as it appended a white-backgrounded "%" sign after the actual random hash, but because my shell is generally behaving strange on some occasions, I didn't want to make this look bad before it was accepted :)
LukeN
I tried this on a Mac, which has a /dev/urandom. Executing the command in a bash shell causes an error - 'tr: Illegal byte sequence'
Gareth Stockwell
I think the problem here is that BSD and Mac's interpret the string as being multibyte instead of single byte. I haven't got a machine to try this on, so report back here if this works:cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-f0-9' | head -c 32
fmark
@fmark: your modified version works perfectly on my Snow Leopard bash.
GregS
+7  A: 

why do not use unix mktemp command:

$ TMPFILE=`mktemp tmp.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` &&  echo $TMPFILE
tmp.MnxEsPDsNUjrzDIiPhnWZKmlAXAO8983
oraz
A: 

Grab 16 bytes from /dev/random, convert them to hex, take the first line, remove the address, remove the spaces.

head /dev/random -c16 | od -tx1 -w16 | head -n1 | cut -d' ' -f2- | tr -d ' '

Assuming that "without resorting to a program" means "using only programs that are readily available", of course.

Thomas
I tested this out. Its a lot slower then the other solutions, but does work.
DJTripleThreat
Yeah, it's a fairly long pipe for something simple like that. I'd hoped for some more flags on `od`, but couldn't get it to do what I wanted. fmark's answer, though it processes more bytes, might actually be faster.
Thomas
It would be faster if it used /dev/urandom rather than /dev/random
fmark
fmark is right, it could be the /dev/random part here, because /dev/random is a truly random number generator and will block when there's no more entropy :)
LukeN
A: 

As you probably noticed from each of the answers, you generally have to "resort to a program".

However, without using any external executables, in Bash and ksh:

for i in {0..31}; do string+=$(printf "%x" $(($RANDOM%16)) ); done; echo $string

in zsh:

for i in {0..31}; do string+=$(printf "%x" $(($RANDOM%16)) ); dummy=$RANDOM; done; echo $string

Note that because of using the mod operator on a value that ranges from 0 to 32767 the distribution of digits using the snippets above will be skewed (not to mention the fact that the numbers are pseudo random in the first place).

In any case, the correct way to do this is using mktemp as in oraz's answer.

Dennis Williamson