Skip to content

Instantly share code, notes, and snippets.

@masato9000
Last active September 20, 2021 22:34
Show Gist options
  • Save masato9000/d0b5c6be5c63aa75329e2fb614146e62 to your computer and use it in GitHub Desktop.
Save masato9000/d0b5c6be5c63aa75329e2fb614146e62 to your computer and use it in GitHub Desktop.
Generate random text of arbirary length using selectable character sets in POSIX shells
#!/bin/sh
_bsdl='
Copyright (c) 2021, masato9000@users.noreply.github.com
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'
usage() {
echo Generate random strings
echo Usage:
echo "randomchars [-l <length>] [-f <format string>]"
echo "format string options:"
printf "\ta\tinclude lowercase letters a-z\n"
printf "\tA\tinclude uppercase letters A-Z\n"
printf "\td\tinclude digits 0-9\n"
printf "\ts\tinclude symbols\n"
printf "\th\thexadecimal (lowercase) - cannot be combined with \"H\" or \"b\"\n\t\toverrides all other inclusions\n"
printf "\tH\thexadecimal (uppercase) - cannot be combined with \"h\" or \"b\"\n\t\toverrides all other inclusions\n"
printf "\tb\tbase64 (character set) - cannot be combined with \"h\" or \"H\"\n\t\toverrides all other inclusions\n"
printf "\tB\tbase32 (character set) - cannot be combined with \"h\" or \"H\"\n\t\toverrides all other inclusions\n"
echo Default options are: -l 32 -f aAd
exit 1
}
parseargs() {
unset _length _formatstr
while [ $# -gt 0 ]; do
_arg="$1"a
case $_arg in
-l)
[ "$_length" ] && usage
if ( [ "$2" ] && [ "$2" -gt 0 ] 2>/dev/null ); then
_length=$2
else
usage
fi
shift 2
;;
-f)
[ "${_formatstr}" ] && usage
[ "$2" ] || usage
_formatstr=$2
shift 2
;;
*)
usage
;;
esac
done
[ "$_length" ] || _length=32
[ "$_formatstr" ] || _formatstr=aAd
}
parse_format() {
_formatstr=$1
unset do_a do_A do_d do_s do_h do_H do_b
OPTIND=1
while getopts "aAdshHbB" _opt
do
case $_opt in
a) do_a=y;;
A) do_A=y;;
d) do_d=y;;
s) do_s=y;;
h) do_h=y;;
H) do_H=y;;
b) do_b=y;;
B) do_B=y;;
[?]) usage
;;
esac
done
[ "$do_b" ] && ( [ "$do_B" ] || [ "$do_h" ] || [ "$do_H" ] ) && usage
[ "$do_B" ] && ( [ "$do_h" ] || [ "$do_H" ] ) && usage
( [ "$do_h" ] && [ "$do_H" ] ) && usage
( [ "$do_h" ] || [ "$do_H" ] || [ "$do_b" ] || [ "$do_B" ] ) && unset do_a do_A do_d do_s
}
mkcharset() {
_value=$1
while [ $_value -le $2 ]; do
printf "%3.3o " $_value
_value=$(($_value + 1))
done
printf "\n"
}
buildcharset() {
# Enumerate character sets as space-separated octal values
# eliminating the need to deal with special characters
[ "$do_a" ] && a_set=$(mkcharset 97 122)
[ "$do_A" ] && A_set=$(mkcharset 65 90)
[ "$do_d" ] && d_set=$(mkcharset 48 57)
[ "$do_s" ] && s_set="$(mkcharset 33 47)$(mkcharset 58 64)$(mkcharset 91 96)$(mkcharset 123 126)"
echo ${a_set}${A_set}${d_set}${s_set}
}
countargs() {
echo $#
}
getrandchar() {
_index=$1
shift $_index
echo $1
}
parseargs "$@"
parse_format -$_formatstr
if [ "$do_b" ]; then
dd if=/dev/urandom bs=1 count=$_length 2</dev/null | base64 | \
cut -b 1-$_length
elif [ "$do_B" ]; then
dd if=/dev/urandom bs=1 count=$_length 2</dev/null | base32 | \
cut -b 1-$_length
elif [ "$do_h" ]; then
od -vAn -x -N $(((_length + 1)-2)) </dev/urandom | \
tr -d '[:space:]' | cut -b 1-$_length
elif [ "$do_H" ]; then
od -vAn -x -N $(((_length + 1)-2)) </dev/urandom | \
tr -d '[:space:]' | tr '[:lower:]' '[:upper:]' | \
cut -b 1-$_length
else
char_set=$(buildcharset)
set_size=$(countargs $char_set)
# Our largest possible output character set is n=94 characters. 256 modulus n
# can be too unfairly weighted against characters later in the set.
# 2-byte numbers ( 65536 % n ) should balance that out a bit more
randombytes=$(od -vAn -x -N $((_length * 2)) </dev/urandom)
for _randval in $randombytes; do
_char=$(getrandchar $((0x$_randval % $set_size + 1)) $char_set)
printf "\\$_char"
done
printf "\n"
fi
@masato9000
Copy link
Author

masato9000 commented Apr 18, 2021

Probably not much novel here but I got tired of looking up online generators in a web browser whenever I needed a random string in a particular format.

  • Settable output lenght
  • Settable output charsets
    • lowercase letters
    • UPPERCASE LETTERS
    • digits 0 - 9
    • All other ASCII printable symbols (non-whitespace)
    • OR hexadecimal lowercase
    • OR hexadecimal uppercase
    • OR base64 character set
    • OR base32 character set

Defaults (no args) outputs 32-characters of mixed-case alphanumeric text.
Largest output character set includes mixed-case, decimals and symbols (base94 charset)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment