Skip to content

Instantly share code, notes, and snippets.

@pmarreck
Last active June 29, 2022 13:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pmarreck/f4dbb02396c4532762a7a64511cd8e52 to your computer and use it in GitHub Desktop.
Save pmarreck/f4dbb02396c4532762a7a64511cd8e52 to your computer and use it in GitHub Desktop.
Generate random strings or dictionary words in Bash
#!/usr/bin/env bash
# get a random-character password
# First argument is password length
# Can override the default character set by passing in PWCHARSET=<charset> as env
randompass() {
# globbing & history expansion here is a pain, so we store its state, temp turn it off & restore it later
local maybeglob="$(shopt -po noglob histexpand)"
set -o noglob # turn off globbing
set +o histexpand # turn off history expansion
if [ $# -eq 0 ]; then
echo "Usage: randompass <length>"
return 1
fi
# allow overriding the password character set with env var PWCHARSET
# NOTE that we DELETE THE CAPITAL O, CAPITAL I AND LOWERCASE L CHARACTERS
# DUE TO SIMILARITY TO 1 AND 0
# (but only if you use the default alnum set)
# BECAUSE WHO THE FUCK EVER THOUGHT THAT WOULD BE A GOOD IDEA? 😂
if [ -z "$PWCHARSET" ]; then
local lower=$(echo -n {a..z} | tr -d ' ')
local upper=$(echo -n {A..Z} | tr -d ' ')
local num=$(echo -n {0..9} | tr -d ' ')
local alcharacterset="$lower$upper"
local alnumcharacterset=$(printf "%s" "$alcharacterset$num" | tr -d 'OlI')
local punc='!@#$%^&*-_=+[]{}|;:,.<>/?~'
local PWCHARSET="$alnumcharacterset$punc"
fi
# ...but also intersperse it with spaces so that the -e option to shuf works.
# Using awk to split the character set into a space-separated string of characters.
# Saw some noise that empty field separator will cause awk problems,
# but it's concise and fast and works, so... &shrug;
# printf is necessary due to some of the punctuation characters being interpreted when using echo
local characterset=$(printf "%s" "$PWCHARSET" | awk NF=NF FS="")
{ shuf --random-source=/dev/urandom -n $1 -er $characterset; } | tr -d '\n'
echo
# restore any globbing state
eval "$maybeglob"
# cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9\!\@\#\$\%\&\*\?' | fold -w $1 | head -n 1
}
# get a random-dictionary-word password
# First argument is minimum word length
# Second argument is number of words to generate
randompassdict() {
if [ $# -eq 0 ]; then
echo "Usage: randompassdict <num-words> [<min-word-length> [<max-word-length>]]"
return 1
fi
local numwords=$1
local minlen=${2:-8}
local maxlen=${3:-99}
# take the dict, filter out anything not within the min/max length or that has apostrophes, and shuffle
local pool=$(cat /usr/share/dict/words | awk 'length($0) >= '$minlen' && length($0) <= '$maxlen' && $0 ~ /^[^'\'']+$/')
local poolsize=$(printf "%s" "$pool" | wc -l)
# why is poolsize getting spaces in front? No idea. Removing them.
poolsize=${poolsize##* }
local words=$(echo -n "$pool" | shuf -n "$numwords" | tr '\n' ' ')
echo "$words"
echo "(out of a possible $poolsize available words in the dictionary that suit the requested length range [$minlen-$maxlen])" 1>&2
# a former attempt that worked but was less flexible:
#cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9\!\@\#\$\%\&\*\?' | fold -w $1 | head -n $2 | tr '\n' ' '
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment