Skip to content

Commit da92258

Browse files
committed
chore(scripts): add random string generator utility
1 parent 8115344 commit da92258

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ clean: ## Remove build and coverage artifacts
7272
stress: docker-stack ## Run stress test script
7373
./scripts/stress.sh
7474

75+
gen-random: ## Generate random string of specified length (default: 32)
76+
./scripts/gen-random-string.sh $(length)
77+
7578
##@ Docker
7679
docker-build: ## Build Docker image
7780
$(DOCKER) build -t $(DOCKER_IMAGE):$(DOCKER_TAG) .

scripts/gen-random-string.sh

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Generate a random string from lower/upper letters, digits, and symbol set.
5+
#
6+
# Usage:
7+
# ./scripts/gen-random-string.sh
8+
# ./scripts/gen-random-string.sh 48
9+
# ./scripts/gen-random-string.sh 48 '!@#$%_-'
10+
#
11+
# Defaults:
12+
# length = 32
13+
# symbols = !@#$%^&*()-_=+[]{}.,:;?
14+
15+
DEFAULT_LENGTH=32
16+
DEFAULT_SYMBOLS='!@#$%^&*()-_=+[]{}.,:;?'
17+
18+
LOWER='abcdefghijklmnopqrstuvwxyz'
19+
UPPER='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
20+
DIGITS='0123456789'
21+
22+
usage() {
23+
cat <<USAGE
24+
Generate random string.
25+
26+
Usage:
27+
$(basename "$0") [length] [symbols]
28+
29+
Arguments:
30+
length Positive integer. Default: ${DEFAULT_LENGTH}
31+
symbols Symbol set to include. Default: ${DEFAULT_SYMBOLS}
32+
33+
Notes:
34+
- For length >= 4, output guarantees at least one lowercase, uppercase,
35+
digit, and symbol character.
36+
- For length < 4, output is sampled from the full charset without guarantees.
37+
USAGE
38+
}
39+
40+
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
41+
usage
42+
exit 0
43+
fi
44+
45+
length="${1:-$DEFAULT_LENGTH}"
46+
symbols="${2:-$DEFAULT_SYMBOLS}"
47+
48+
if ! [[ "$length" =~ ^[0-9]+$ ]] || [[ "$length" -le 0 ]]; then
49+
echo "length must be a positive integer" >&2
50+
exit 2
51+
fi
52+
53+
if [[ -z "$symbols" ]]; then
54+
echo "symbols must be non-empty" >&2
55+
exit 2
56+
fi
57+
58+
full_charset="${LOWER}${UPPER}${DIGITS}${symbols}"
59+
60+
rand_u32() {
61+
od -An -N4 -tu4 /dev/urandom | tr -d ' \n'
62+
}
63+
64+
rand_index() {
65+
local n="$1"
66+
local r
67+
r="$(rand_u32)"
68+
echo $(( r % n ))
69+
}
70+
71+
pick_from() {
72+
local set="$1"
73+
local idx
74+
idx="$(rand_index "${#set}")"
75+
printf '%s' "${set:idx:1}"
76+
}
77+
78+
shuffle_chars() {
79+
local -n arr_ref=$1
80+
local i j tmp
81+
for (( i=${#arr_ref[@]}-1; i>0; i-- )); do
82+
j="$(rand_index $((i+1)))"
83+
tmp="${arr_ref[i]}"
84+
arr_ref[i]="${arr_ref[j]}"
85+
arr_ref[j]="$tmp"
86+
done
87+
}
88+
89+
out_chars=()
90+
91+
if (( length >= 4 )); then
92+
out_chars+=("$(pick_from "$LOWER")")
93+
out_chars+=("$(pick_from "$UPPER")")
94+
out_chars+=("$(pick_from "$DIGITS")")
95+
out_chars+=("$(pick_from "$symbols")")
96+
fi
97+
98+
while (( ${#out_chars[@]} < length )); do
99+
out_chars+=("$(pick_from "$full_charset")")
100+
done
101+
102+
shuffle_chars out_chars
103+
104+
printf '%s\n' "${out_chars[*]}" | tr -d ' '

0 commit comments

Comments
 (0)