What's new

Script for creating random numbers, and more

  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

smasher

Regular Contributor
Script for creating random numbers, hex strings, octal strings, random passwords...

This is one of those scripts I should have written a long time ago.
  • 32-character hex string: randoms -f 32
  • 32-character hex string with upper-case letters: randoms -F 32
  • 50 random decimal numbers, 8 digits each: randoms 8 50
  • 12 character random password: randoms -p 12
  • 5 random MAC addresses: randoms -F 12 5 | sed 's!.\{2\}!&:!g ; s!:$!!'
  • random (but probably invalid) UUID: randoms -f 32 | sed -E 's/(.{8})(.{4})(.{4})(.{4})/\1-\2-\3-\4-/'
  • (valid) random UUID (of a known or unknown variant; DCE, NCS, Microsoft, undefined): randoms -f 31 | sed -E 's/(.{8})(.{4})(.{3})(.{4})/\1-\2-4\3-\4-/'
This can be used to print arbitrarily long random strings, and/or an arbitrary number of random strings.
Code:
#!/bin/sh

## atom's "randoms" script
## v1.01, 18 Nov 2019, (c) atom(@)smasher.org
## v1.01f, 09 may 2022, (c) atom(@)smasher.org
## Distributed under the GNU General Public License
## http://www.gnu.org/copyleft/gpl.html
## originally posted - https://www.snbforums.com/threads/script-for-creating-random-numbers-and-more.60182/

## test for output format option
## thanks Dabombber for this section of code :)
case "$1" in
    '-f')
    ## this gives hexadecimal output
    chars_match='0-9a-f'
    shift
    ;;
    '-F')
    ## this gives hexadecimal output, upper-case letters
    chars_match='0-9A-F'
    shift
    ;;
    '-8')
    ## this gives octal output
    chars_match='0-7'
    shift
    ;;
    '-p')
    ## random passwords
    chars_match='0-9a-zA-Z<>/\?!@#$%^&*()+=_,.-'
    ## for random passwords, edit the character-set as desired
    ## nb, '!-~' include all "normal" "printable" ASCII characters
    shift
    ;;
    *)
    ## default is decimal output
    chars_match='0-9'
    ;;
esac

#### 1st argument
## $1 is number of digits output per line.
## 18 is a default, because that's what usually what expr can safely handle.
## this makes it easy to use this script for deriving random numbers within
##     a given range, eg for getting random numbers between 1-37, inclusive:
##     expr $( randoms ) % 37 + 1
digits_output=${1:-18}

#### 2nd argument
## $2 is number of output lines
## defaults to 1
lines_output=${2:-1}

## this displays on "-h", "--help", or any improper use
help () {
    echo "$(basename $0): usage:"
    echo "    $(basename $0) [-f|-F|-8|-p] [d [l]]"
    echo
    echo '    -f: hexadecimal output'
    echo '    -F: hexadecimal output, with upper-case letters'
    echo '    -8: octal output'
    echo '    -p: random passwords'
    echo '    d: number of digits (or characters) output per line'
    echo '    l: number of output lines'
    echo '    ** Default output is decimal format, 18 digits, 1 line'
    echo
    echo 'Examples:'
    echo '* Print 5 random decimal strings, each with 3 digits:'
    echo "    $(basename $0) 3 5"
    echo
    echo '* Print 1 random hexadecimal string with 32 digits:'
    echo "    $(basename $0) -f 32"
    echo
    echo '* Print 1 random decimal string with 300 digits:'
    echo "    $(basename $0) 300"
    echo
    echo '* Print 1 random password string with 12 characters:'
    echo "    $(basename $0) -p 12"
    echo
    echo "* Print 5 random MAC addresses:"
    echo "    $(basename $0) -F 12 5 | sed 's!.\{2\}!&:!g ; s!:\$!!' "
    echo
    echo "* Print a random (but probably invalid) UUID:"
    printf "    $(basename $0) -f 32 | sed -E 's/(.{8})(.{4})(.{4})(.{4})/\\\1-\\\2-\\\3-\\\4-/'\n"
    echo
    echo "* Print a (valid) random UUID (of a known or unknown variant; DCE, NCS, Microsoft, undefined):"
    printf "    $(basename $0) -f 31 | sed -E 's/(.{8})(.{4})(.{3})(.{4})/\\\1-\\\2-4\\\3-\\\4-/'\n"
    echo

    exit 1
}

## test that numeric arguments are sensible
## or print help (to stderr) and exit (exit status 1)
[ 0 -lt ${digits_output} ] 2> /dev/random || help 1>&2
[ 0 -lt ${lines_output}  ] 2> /dev/random || help 1>&2

n=1
## go through this loop once per line of output
while [ ${lines_output} -ge ${n} ]
do
    ## this gives output strings of arbitrary length
    tr -cd "${chars_match}" < /dev/urandom | head -c ${digits_output}
    echo
    n=$(( $n + 1 ))
done

exit 0

####################################
## NOTE THE "exit" DIRECTLY ABOVE ##
## WHAT'S BELOW HERE WILL NOT RUN ##
####################################

## for systems with wonky /dev/urandom, this (below) may be more robust.
## it's actually concerning how often "openssl enc -rc4" does better
##    than /dev/urandom, according to dieharder, although there are still
##    advantages to using /dev/urandom (such as portability and periodic reseeding)
## this is included here for reference and experimentation. have fun with it.
### rc4 is fast and does a good job passing the dieharder tests.
### "tail -c +17" strips out the "Salted__xxxxxxxx" string.
### "2> /dev/random" pipes openssl's errors into /dev/random; use /dev/null if needed.
### "head -c 16 /dev/urandom" derives a 128 bit password, read by "-pass stdin".
n=1
head -c 16 /dev/urandom | openssl enc -rc4 -pass stdin -in /dev/zero 2> /dev/random | tail -c +17 | while :
do
    ## go through this loop once per line of output
    ## this gives output strings of arbitrary length
    tr -cd "${chars_match}" | head -c ${digits_output}
    echo
    n=$(( $n + 1 ))
    [ ${lines_output} -ge ${n} ] || exit 0
done

##############################################
## and another alternative to /dev/urandom, ##
## this time using "haveged"                ##
##############################################
## stderr is directed from haveged into /dev/random,
##    if needed, direct that into /dev/null
n=1
## go through this loop once per line of output
haveged -n 0 2> /dev/random | while [ ${lines_output} -ge ${n} ]
do
    ## this gives output strings of arbitrary length
    tr -cd "${chars_match}" | head -c ${digits_output}
    echo
    n=$(( $n + 1 ))
done

nb the RT-AC86U (Merlin 384.13) does not have rc4. If you want to use that part of the script, use rc5 instead. As it is, it runs perfectly well on the AC68U, and it's also great on other Linux/BSD/unix machines.
 
Last edited:
To modify this for passwords (or other strings) that include all printable ASCII characters, set "chars_match='!-~'", which is equivalent to:
Code:
 tr -cd '!-~' < /dev/urandom
 
Nice. A while back when I needed a random hex string I ran into a bunch of linux/asuswrt oddities, this would have been handy then.

A couple of things, the hex variable seems like a pointless intermediary step when you could set the characters directly, something like
Code:
case "$1" in
    '-f')
        ## this gives hexadecimal output
        chars_match='0-9a-f'
        shift
    ;;
    '-F')
        ## this gives hexadecimal output, upper-case letters
        chars_match='0-9A-F'
        shift
    ;;
    '-8')
        ## this gives octal output
        chars_match='0-7'
        shift
    ;;
    '-p')
        ## random passwords
        chars_match='0-9a-zA-Z<>/\?!@#$%^&*()+=_,.-'
        shift
    ;;
    *)
        ## default is decimal output
        chars_match='0-9'
    ;;
esac

Should [[d] l] be [d [l]]? Since the digits can be set without lines but not vice versa (https://en.wikipedia.org/wiki/Usage_message#Examples).

I think printable characters also includes space and the class without the space is graphable characters, but that's just semantics. It's a shame asuswrt's tr isn't compiled with character classes enabled, but even then busybox's implementation seems to ignore [: print:] and [:graph:].
 
A couple of things, the hex variable seems like a pointless intermediary step when you could set the characters directly, something like
Cool! Updated! Thanks for that! I was thinking about either a case statement or getopts, but then I forgot about it.

Should [[d] l] be [d [l]]? Since the digits can be set without lines but not vice versa (https://en.wikipedia.org/wiki/Usage_message#Examples).
Good catch! Got that updated, too!

I think printable characters also includes space and the class without the space is graphable characters, but that's just semantics.
Yeah, I was pondering that... I was thinking of "printable" in the sense that it consumes ink, on a page. I did make some updates to the comments about that.

Weird that "space" (0x20) is technically a "printable character", but "horizontal tab" (0x09) is technically a "control character". In any case, anyone can edit that to include spaces, tabs, or even escapes, deletes, backspaces, and other characters that may not be intuitive to type into a web-site login page ;)

https://en.wikipedia.org/wiki/ASCII#Printable_characters

Anyway, spaces are a part of strong passwords, but those are different types of passwords - https://xkcd.com/936/ - I do have a script that makes those types of passwords, but it relies on dictionary files, so it's not feasible for a typical busybox device.

It's a shame asuswrt's tr isn't compiled with character classes enabled, but even then busybox's implementation seems to ignore [: print:] and [:graph:].
Yeah, that would be convenient, but we can do it the hard way.

Thanks :)
 

Similar threads

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top