Not worth creating a project for, and it might be interesting to see what changes people would make.
Non-standard dependencies:
#!/usr/bin/zsh
# Author: @[email protected]
# 2025-02-23
final=(xargs echo)
count=6
while getopts d opt; do
case $opt in
d)
final=(tr 'A-Z' 'a-z')
;;
*)
printf "Password generator based on the correcthorse algorithm from http://xkcd.com/936//n/n"
printf "USAGE: %s [-d] [#]\n" "$0"
printf " -d make the result all lower case; otherwise, each word will be capitalized.\n"
printf " # the number of words to include. Defaults to 6."
exit 1
;;
esac
done
shift $(($OPTIND - 1))
[[ $# -gt 0 ]] && count=$*
shuf -n $((count * 2)) /usr/share/dict/american-english | \
sed 's/'"'"'.*//; s/^\(\w\)/\U\1/' | \
sort | uniq | shuf -n $count | xargs echo | \
tr -d ' ' | $final
What’s going on here:
Nearly 30% of the American dictionary (34,242) are words with apostrophes. They could be left in to help satisfy password requirements that demand “special characters,” but correcthorse isn’t an algorithm that handles idiot “password best practices” well anyway. So, since every word with an apostrophe has a pair word without one, we pull 2·N words to make sure we have enough. Then we strip out the plural/possessives and capitalize every word. Then we remove duplicates and select our N words from the result. Finally, we compact that into a space-less string of words, and if the user passed the -d
option, we downcase the entire thing.
Without the user options, this really could be a 1-liner; that’s how it started:
alias pony="shuf -n 12 /usr/share/dict/american-english | sed 's/'\"'\"'.*//; s/^\(\w\)/\U\1/' | sort | uniq | shuf -n 6 | xargs echo | tr -d ' '"
That’s the one that made me make this script. I thought, surely this is a one-liner? And indeed, except that I kept adding features. I think I’m going to change my shell function back to the one-liner though. All of that extra complexity is unnecessary.