December 12, 2012

A bashism a week: $RANDOM numbers

Commonly used to sleep a random amount of time or to create unique temporary file names, $RANDOM is one of those bashisms that you are best avoiding it altogether.

It is not uncommon to see scripts generating a "unique" temporary file name with code that goes like: tempf="/tmp/foo.$RANDOM", or tempf="/tmp/foo.$$.$RANDOM".

Under some shells the "unique" temporary file name will be "/tmp/foo." for the first example code. So much for randomness, right?

Even if you go around it by defining $RANDOM to the output of cksum after reading some bytes from /dev/urandom, please: don't do that. Use the mktemp command instead.
When creating temporary files there's more than just generating a file name. Just don't do it on your own: use mktemp. Really, use it, the list of those who weren't using mktemp (or similar) is large enough as it is.

Don't even dare to mention the linux kernel-based protection against symlink attacks. There's no excuse for not using mktemp.

Tip: If you are going to use multiple temporary files, create a temporary directory instead. Use mktemp -d.
Tip: Don't reuse a temporary file's name, even if you unlink/remove it. Generate a new one with mktemp.
Tip: Reusing also means doing things like tmp="$(mktemp)"; some_command > "$tmp.stdout" 2> "$tmp.stderr"
Tip: Even if $RANDOM is not empty, don't use it. It could have been exported as an environment variable. Again, just use mktemp.

For the remaining cases where you may want a pseudo random number such as for sleeping a random number of seconds: you can use something as simple as $$. Use shell arithmetic to adjust it as needed: use the modulo operator, multiply it, etc.

If you think you need something more "random" than the process' id, then you should probably not be using $RANDOM in the first place.

4 comments:

  1. Indeed, reminds me of the password generator that used $RANDOM:

    http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=659047

    ReplyDelete
  2. http://codesearch.debian.net/search?q=\%24RANDOM+filetype%3Ashell

    It seems that debian has a few offenders

    ReplyDelete
    Replies
    1. Not all of these are necessarily offenders – for example, if they have #!/bin/mksh they aren’t… except if it occurs in a line with tmp… there’s indeed no excuse for not using mktemp(1).

      Delete
  3. Hi,
    as an addendum to the advice about mktemp: let's be sure to remove the temporary files in the "proper way":

    TMP="$(mktemp)"
    trap 'rm -f "$TMP" >/dev/null 2>&1' 0
    trap "exit 2" 1 2 3 15
    ...

    from http://www.shelldorado.com/goodcoding/tempfiles.html

    BTW randomness and uniqueness are different things: if I pick two numbers randomly they CAN be the same number, if I want unique numbers I can use a strictly monotonic function which is surely not random at all.

    So, to make your argument even stronger: using random numbers as unique identifiers is wrong as a _policy_, regardless of the mechanism :)

    Sometimes you want unique like in the temp file example, but some other times you really want random and PIDs are usually not good enough as they are designed for uniqueness, in that case using /dev/urandom in some way looks OK to me if you need _portability_.

    The way I see it, the argument against $RANDOM for random numbers is not about quality, it is about context: $RANDOM is just not portable, but there's no harm to use it where it is properly supported.

    ReplyDelete