rand_pass.sh
· 3.2 KiB · Bash
Raw
#!/bin/bash
set -euo pipefail
# ── Defaults ─────────────────────────────────────────────────────────
DEFAULT_LENGTH=16
CHARSET="alnum" # alnum | ascii
# ── Usage ────────────────────────────────────────────────────────────
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS] [LENGTH]
Generate a random password of the given length (default: ${DEFAULT_LENGTH}).
Options:
-h, --help Show this help
-s, --special Include special characters (!@#\$%^&*_+-=)
-c, --copy Copy password to clipboard (requires wl-copy)
Examples:
$(basename "$0") # 16-char alphanumeric password
$(basename "$0") 32 # 32-char alphanumeric password
$(basename "$0") -s 24 # 24-char password with special characters
$(basename "$0") -sc 20 # 20-char with specials, copied to clipboard
EOF
exit 1
}
# ── Parse arguments ──────────────────────────────────────────────────
FLAG_COPY=""
while (( $# )); do
case "$1" in
-h|--help) usage ;;
-s|--special) CHARSET="ascii"; shift ;;
-c|--copy) FLAG_COPY=1; shift ;;
-sc|-cs) CHARSET="ascii"; FLAG_COPY=1; shift ;;
-*) echo "Unknown option: $1" >&2; usage ;;
*) break ;;
esac
done
LENGTH="${1:-$DEFAULT_LENGTH}"
# ── Validate length ─────────────────────────────────────────────────
if ! [[ "$LENGTH" =~ ^[0-9]+$ ]] || (( LENGTH <= 0 )); then
echo "Error: length must be a positive integer, got '${LENGTH}'" >&2
exit 1
fi
# ── Generate password ───────────────────────────────────────────────
# tr character classes:
# alnum -> [A-Za-z0-9]
# ascii -> [A-Za-z0-9] + special characters
generate_password() {
local len="$1"
local filter
case "$CHARSET" in
alnum) filter='A-Za-z0-9' ;;
ascii) filter='A-Za-z0-9!@#$%^&*_+\-=' ;;
esac
# Read enough random bytes to guarantee the desired length after filtering.
# tr may exit with SIGPIPE once head has enough bytes — that is expected.
LC_ALL=C tr -dc "$filter" </dev/urandom | head -c "$len" || true
}
PASSWORD="$(generate_password "$LENGTH")"
# ── Output ───────────────────────────────────────────────────────────
echo "$PASSWORD"
# ── Copy to clipboard ───────────────────────────────────────────────
if [[ -n "$FLAG_COPY" ]]; then
if command -v wl-copy &>/dev/null; then
echo -n "$PASSWORD" | wl-copy
echo "(copied to clipboard)" >&2
else
echo "Warning: wl-copy not found (install wl-clipboard)" >&2
fi
fi
| 1 | #!/bin/bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | # ── Defaults ───────────────────────────────────────────────────────── |
| 5 | DEFAULT_LENGTH=16 |
| 6 | CHARSET="alnum" # alnum | ascii |
| 7 | |
| 8 | # ── Usage ──────────────────────────────────────────────────────────── |
| 9 | usage() { |
| 10 | cat <<EOF |
| 11 | Usage: $(basename "$0") [OPTIONS] [LENGTH] |
| 12 | |
| 13 | Generate a random password of the given length (default: ${DEFAULT_LENGTH}). |
| 14 | |
| 15 | Options: |
| 16 | -h, --help Show this help |
| 17 | -s, --special Include special characters (!@#\$%^&*_+-=) |
| 18 | -c, --copy Copy password to clipboard (requires wl-copy) |
| 19 | |
| 20 | Examples: |
| 21 | $(basename "$0") # 16-char alphanumeric password |
| 22 | $(basename "$0") 32 # 32-char alphanumeric password |
| 23 | $(basename "$0") -s 24 # 24-char password with special characters |
| 24 | $(basename "$0") -sc 20 # 20-char with specials, copied to clipboard |
| 25 | EOF |
| 26 | exit 1 |
| 27 | } |
| 28 | |
| 29 | # ── Parse arguments ────────────────────────────────────────────────── |
| 30 | FLAG_COPY="" |
| 31 | |
| 32 | while (( $# )); do |
| 33 | case "$1" in |
| 34 | -h|--help) usage ;; |
| 35 | -s|--special) CHARSET="ascii"; shift ;; |
| 36 | -c|--copy) FLAG_COPY=1; shift ;; |
| 37 | -sc|-cs) CHARSET="ascii"; FLAG_COPY=1; shift ;; |
| 38 | -*) echo "Unknown option: $1" >&2; usage ;; |
| 39 | *) break ;; |
| 40 | esac |
| 41 | done |
| 42 | |
| 43 | LENGTH="${1:-$DEFAULT_LENGTH}" |
| 44 | |
| 45 | # ── Validate length ───────────────────────────────────────────────── |
| 46 | if ! [[ "$LENGTH" =~ ^[0-9]+$ ]] || (( LENGTH <= 0 )); then |
| 47 | echo "Error: length must be a positive integer, got '${LENGTH}'" >&2 |
| 48 | exit 1 |
| 49 | fi |
| 50 | |
| 51 | # ── Generate password ─────────────────────────────────────────────── |
| 52 | # tr character classes: |
| 53 | # alnum -> [A-Za-z0-9] |
| 54 | # ascii -> [A-Za-z0-9] + special characters |
| 55 | generate_password() { |
| 56 | local len="$1" |
| 57 | local filter |
| 58 | |
| 59 | case "$CHARSET" in |
| 60 | alnum) filter='A-Za-z0-9' ;; |
| 61 | ascii) filter='A-Za-z0-9!@#$%^&*_+\-=' ;; |
| 62 | esac |
| 63 | |
| 64 | # Read enough random bytes to guarantee the desired length after filtering. |
| 65 | # tr may exit with SIGPIPE once head has enough bytes — that is expected. |
| 66 | LC_ALL=C tr -dc "$filter" </dev/urandom | head -c "$len" || true |
| 67 | } |
| 68 | |
| 69 | PASSWORD="$(generate_password "$LENGTH")" |
| 70 | |
| 71 | # ── Output ─────────────────────────────────────────────────────────── |
| 72 | echo "$PASSWORD" |
| 73 | |
| 74 | # ── Copy to clipboard ─────────────────────────────────────────────── |
| 75 | if [[ -n "$FLAG_COPY" ]]; then |
| 76 | if command -v wl-copy &>/dev/null; then |
| 77 | echo -n "$PASSWORD" | wl-copy |
| 78 | echo "(copied to clipboard)" >&2 |
| 79 | else |
| 80 | echo "Warning: wl-copy not found (install wl-clipboard)" >&2 |
| 81 | fi |
| 82 | fi |