Skip to content

Instantly share code, notes, and snippets.

@oleasteo
Last active November 27, 2024 10:52
Show Gist options
  • Select an option

  • Save oleasteo/1936b012b3f88a86ce92036c9110c5f4 to your computer and use it in GitHub Desktop.

Select an option

Save oleasteo/1936b012b3f88a86ce92036c9110c5f4 to your computer and use it in GitHub Desktop.
#!/bin/bash
cmd="$0"
usage() {
cat <<EOL
Usage: $cmd [...OPTIONS] [LENGTH=16]
Generates a random string based on the specified alphabet. If no alphabet is
specified, the \`base\` alphabet is used (alphanumeric and special characters).
OPTIONS
-h | --help Show this message.
-v | --verbose Print charset (as passed to \`tr\`).
-c | --charset CHARS Use custom alphabet. See \`tr\` for syntax.
-n | --length LENGTH Define the output length (default: 16).
-[ludseqxrmabA] | --[ALPHABET]
Use the respective alphabet.
ALPHABETS
-l | --lower a-z
-u | --upper A-Z
-d | --digit 0123456789
-s | --special !@#%^&*()+_=,.?-
-e | --extra \| ;:/<>[]{}$
-q | --quote '"\`
-x | --hex 0-9A-F
-r | --url A-Za-z0-9_-
-m | --unambiguous aCDdEeFfGgHhJKLMmNnPQRrTtWY23489
-a | --alpha Equivalent to \`-lu\`. (lower & upper)
-b | --base Equivalent to \`-luds\` (alpha & digit & special)
-A | --all Equivalent to \`-ludseq\` (base & extra & quote)
EXAMPLES
$cmd --all 8
Generate a random string with 8 characters, using all builtin alphabets.
$cmd -ad
Generate a random alphanumeric string with 16 characters.
$cmd -m 12
Generate a random alphanumeric string with 12 unambiguous characters.
$cmd -n 4 -c 0-7
Generate 4 random octal digits.
EOL
}
###
# Options
###
O_LENGTH=16
O_CHARS=
O_VERBOSE=0
declare -A O_USE # { [ALPHABET]: 0|1 }
###
# Constants
###
declare -A CHARSET # { [ALPHABET]: CHARACTERS }
CHARSET[lower]='a-z'
CHARSET[upper]='A-Z'
CHARSET[digit]='0-9'
CHARSET[special]='!@#%^&*()\-+_=,.?'
CHARSET[extra]='\\| ;:/<>\[\]{}$'
CHARSET[quote]="'"'"`'
CHARSET[hex]="0-9A-F"
CHARSET[url]="A-Za-z0-9_\-"
CHARSET[unambiguous]="aCDdEeFfGgHhJKLMmNnPQRrTtWY23489"
declare -A ALIAS # { [ALIAS]: (...alphabet) }
ALIAS[alpha]="lower upper"
ALIAS[base]="${ALIAS[alpha]} digit special"
ALIAS[all]="${ALIAS[base]} extra quote"
declare -A SHORT # { [ALPHABET]: CHAR }
SHORT[lower]=l
SHORT[upper]=u
SHORT[digit]=d
SHORT[special]=s
SHORT[extra]=e
SHORT[quote]=q
SHORT[hex]=x
SHORT[url]=r
SHORT[unambiguous]=m
SHORT[alpha]=a
SHORT[base]=b
SHORT[all]=A
_set_use() { # _set_use 0|1 [...ALPHABET]
if [ $# -eq 1 ]; then
_set_use $1 "${!CHARSET[@]}"
return
fi
local value=$1
shift
local name
for name in "$@"; do O_USE[$name]=$value; done
}
_set_use 0
_set_use 1 ${ALIAS[base]}
###
# Parse Options
###
_opts_short() { local ch; for ch in "${SHORT[@]}"; do echo -n "$ch"; done }
_opts_long() {
local name; for name in "${!CHARSET[@]}"; do echo -n ",$name"; done
local alias; for alias in "${!ALIAS[@]}"; do echo -n ",$alias"; done
}
ARGS=$(getopt -o '+'hvc:n:$(_opts_short) --long "help,verbose,charset,length$(_opts_long)" -- "$@")
[[ $? -ne 0 ]] && exit 1
eval set -- "$ARGS"
###
# Process Options
###
_is_custom=0
_set_custom() {
if [ $_is_custom -eq 0 ]; then
_set_use 0
_is_custom=1
fi
}
while true; do
for name in "${!CHARSET[@]}"; do
short="${SHORT[$name]}"
if [ "$1" = "-$short" -o "$1" = "--$name" ]; then
_set_custom
_set_use 1 $name
shift
continue 2
fi
done
for name in "${!ALIAS[@]}"; do
short="${SHORT[$name]}"
if [ "$1" = "-$short" -o "$1" = "--$name" ]; then
_set_custom
_set_use 1 ${ALIAS[$name]}
shift
continue 2
fi
done
case "$1" in
-h|--help)
usage
exit 0
;;
-v|--verbose)
shift
O_VERBOSE=1
;;
-c|--charset)
shift
_set_custom
O_CHARS="$1"
shift
;;
-n|--length)
shift
O_LENGTH="$1"
shift
if ! [[ "$O_LENGTH" =~ ^[0-9]+$ ]]; then
echo "Error: LENGTH must be a positive integer."
usage
exit 1
fi
;;
--)
shift
break
;;
*)
echo "Error: Unknown option $1"
usage
exit 1
;;
esac
done
if [ $# -gt 1 ]; then
echo "Error: Too many arguments: $@"
exit 1
fi
if [ $# -eq 1 ]; then
O_LENGTH="$1"
shift
if ! [[ "$O_LENGTH" =~ ^[0-9]+$ ]]; then
echo "Error: LENGTH must be a positive integer."
usage
exit 1
fi
fi
###
# Collect chars
###
for name in "${!CHARSET[@]}"; do
if [ "$(echo "${O_USE[$name]}")" -eq 1 ]; then
O_CHARS="$O_CHARS${CHARSET[$name]}"
fi
done
if [ -z "$O_CHARS" ]; then
echo no chars selected
exit 1
fi
###
# Print secret
###
[ -t 1 -a "$O_VERBOSE" -eq 1 ] && echo "Charset: $O_CHARS" && echo -ne "Secret: \033[47;30;1m"
LC_ALL=C tr -dc "$O_CHARS" < /dev/urandom | head -c $O_LENGTH
[ -t 1 ] && echo -e '\033[0m' || true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment