2
\$\begingroup\$

Yesterday, I was bored and decided to write a random password generator. At first it used numerous temporary files, changed that by using arrays.

This was written on OS X and should work on Linux with minor tweaks (at least change all gshuf to shuf).

Example use:

$ pw -c 3 -l 20 -a b -nsr
Vh.9"7`gN;;$kV*/LddE
q);S;byf/._Z8;S1|>Kb
3<*FfS&Y&}wD17l[M?y{

Your thoughts and ideas are welcome.

#!/bin/sh
#
#
# Random Password Generator
#
#

#set -x
COUNT=""
ALPHA=""
NUMERICAL=0
SPECIAL=0
STRING_LENGTH=""
RESHUF=0
# delimiters
nu="0-9"
al="a-z"
au="A-Z"
sc="!\"#$%&\'()*+,-./:;<=>?@[\]^_\`{|}~"

urandom_cat() {
# function generates random characters, delimiter gets send from function gen_char_pw
DELIMITER=$1
    cat /dev/urandom | env LC_CTYPE=C tr -cd $DELIMITER | head -c 1000
}

usage() {
cat << EOF

This script generates a number of passwords.

usage: $0 -c <number> -l <number> -a <option> -nsr

    c and l arguments are REQUIRED!

    at least one argument out of a, n and s is required

ARGUMENTS
   -h       Show this message
   -c number    Number of passwords to create
   -l number    Length of password
   -a u/l/b Use alphabet (upper-case/lower-case/both)
   -n       Use numbers
   -s       Use special characters
   -r       Reshuffle characters of final password (pretty much useless, but mehh, who cares?)


EOF
}

print_pw() {
# length of phrase gets checked 
# in case reshuffle option is ticked, phrase gets reshuffeld and printed out
# else phrase gets printed out. COUNT gets reduced by one
    if [[ "$(printf '%s' "$pw" | wc -m)" -eq $STRING_LENGTH ]] ; then
        if [[ $RESHUF -eq 1 ]] ; then 
            echo "$pw" | fold -w 1 | gshuf | tr -d "\\\r\n" ; echo
        else
            echo "$pw"
        fi
        COUNT=$(($COUNT-1)) 
    fi
}

gen_char_pw() {
# send delimiter to urandom_cat and fills characters into an array
# then random characters from the array get combined to a password
# trailing newline gets cut off and phrase gets cut to desired length
DELIMITER=$1    
    i=0
    for char in "$((urandom_cat $DELIMITER) | fold -w 1)" ; do
        CHARS[$i]=$char
    i=$(($i+1))
    done
    pw="$(gshuf -e "${CHARS[@]}" 2>/dev/null | tr -d "\\\r\n" | head -c $STRING_LENGTH)"
}

gen_pw() {
# lower-case letters
    if [[ $ALPHA == l && $NUMERICAL -eq 0 && $SPECIAL -eq 0 ]] ; then
        gen_char_pw $al
        case "$pw" in
        # contained characters get checked       
            *[a-z]*)
            print_pw
            ;;
        esac
# upper_case letters
    elif [[ $ALPHA == u && $NUMERICAL -eq 0 && $SPECIAL -eq 0 ]] ; then
        gen_char_pw $au 
        case "$pw" in 
            *[A-Z]*)
            print_pw
            ;;
        esac
# numbers
    elif [[ -z $ALPHA && $NUMERICAL -eq 1 && $SPECIAL -eq 0 ]] ; then
        gen_char_pw $nu 
        case "$pw" in 
            *[0-9]*)
            print_pw
            ;;
        esac
# special-characters
    elif [[ -z $ALPHA && $NUMERICAL -eq 0 && $SPECIAL -eq 1 ]] ; then
        gen_char_pw $sc 
        case "$pw" in 
            *[\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~]*)
			print_pw
			;;
		esac
# lower-case and upper-case letters
	elif [[ $ALPHA == b && $NUMERICAL -eq 0 && $SPECIAL -eq 0 ]] ; then
		gen_char_pw $al$au
		case "$pw" in 
			*[A-Z]*)
			case "$pw" in 
				*[a-z]*)
				print_pw
			esac
		esac
# lower-case letters and numbers
	elif [[ $ALPHA == l && $NUMERICAL -eq 1 && $SPECIAL -eq 0 ]] ; then
		gen_char_pw $al$nu
		case "$pw" in 
			*[a-z]*) 
			case "$pw" in 
				*[0-9]*)
				print_pw
				;;
			esac
		esac
# upper-case letters and numbers
	elif [[ $ALPHA == u && $NUMERICAL -eq 1 && $SPECIAL -eq 0 ]] ; then
		gen_char_pw $au$nu
		case "$pw" in 
			*[A-Z]*) 
			case "$pw" in 
				*[0-9]*)
				print_pw
				;;
			esac
		esac
# numbers and special-characters
	elif [[ -z $ALPHA && $NUMERICAL -eq 1 && $SPECIAL -eq 1 ]] ; then
		gen_char_pw $nu$sc
		case "$pw" in 
			*[0-9]*) 
			case "$pw" in 
				*[\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~]*)
                print_pw
                ;;
            esac
        esac
# lower-case letters and special-characters
    elif [[ $ALPHA == l && $NUMERICAL -eq 0 && $SPECIAL -eq 1 ]] ; then
        gen_char_pw $al$sc  
        case "$pw" in 
            *[a-z]*)
            case "$pw" in 
                *[\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~]*)
                    print_pw
            esac
        esac
# upper-case letters and special-characters
    elif [[ $ALPHA == u && $NUMERICAL -eq 0 && $SPECIAL -eq 1 ]] ; then
        gen_char_pw au$sc
        case "$pw" in 
            *[A-Z]*)
            case "$pw" in 
                *[\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~]*)
				print_pw
			esac
		esac
# lower-case letters, numbers and special-characters
	elif [[ $ALPHA == l && $NUMERICAL -eq 1 && $SPECIAL -eq 1 ]] ; then
		gen_char_pw $al$nu$sc	
		case "$pw" in 
			*[0-9]*) 
			case "$pw" in 
				*[a-z]*)
				case "$pw" in 
					*[\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~]*)
                    print_pw
                esac
            esac
        esac
# upper-case letters, numbers and special-characters
    elif [[ $ALPHA == u && $NUMERICAL -eq 1 && $SPECIAL -eq 1 ]] ; then
        gen_char_pw $au$nu$sc
        case "$pw" in 
            *[0-9]*) 
            case "$pw" in 
                *[A-Z]*)
                case "$pw" in 
                    *[\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~]*)
                    print_pw
                esac
            esac
        esac
# lower-case and upper-case letters, numbers
    elif [[ $ALPHA == b && $NUMERICAL -eq 1 && $SPECIAL -eq 0 ]] ; then
        gen_char_pw $al$au$nu
        case "$pw" in 
            *[0-9]*) 
            case "$pw" in 
                *[A-Z]*)
                case "$pw" in 
                    *[a-z]*)
                    print_pw
                esac
            esac
        esac
# lower-case and upper-case letters, special-characters
    elif [[ $ALPHA == b && $NUMERICAL -eq 0 && $SPECIAL -eq 1 ]] ; then
        gen_char_pw $al$au$sc
        case "$pw" in 
            *[A-Z]*)
            case "$pw" in 
                *[a-z]*)
                case "$pw" in 
                    *[\!\@\#\$\%\^\&\*\(\)\_\+\?\>\<\~\`\;\']*)
					print_pw
				esac
			esac
		esac
# lower-case and upper-case letters, numbers and special-characters
	elif [[ $ALPHA == b && $NUMERICAL -eq 1 && $SPECIAL -eq 1 ]] ; then
		gen_char_pw $al$au$nu$sc
		case "$pw" in 
			*[0-9]*) 
			case "$pw" in 
				*[A-Z]*)
				case "$pw" in 
					*[a-z]*)
					case "$pw" in 
						*[\!\@\#\$\%\^\&\*\(\)\_\+\?\>\<\~\`\;\']*)
                        print_pw
                    esac
                esac
            esac
        esac
    fi
}

while getopts c:a:l:nshr opt ; do
    case "$opt" in
    c)  COUNT="$2" ;;
    l)  STRING_LENGTH="$4" ;;
    a)  ALPHA="$6" ;;
    n)  NUMERICAL=1 ;;  
    s)  SPECIAL=1 ;;
    r)  RESHUF=1 ;;
    --) usage
        exit 1 ;;
    h)  usage
        exit 1 ;;
    *)  usage
        exit 1 ;;
    esac
done

if [[ -z $COUNT ]] || [[ -z $STRING_LENGTH ]] ; then
    usage
    exit 1
elif [[ -z $ALPHA ]] && [[ $NUMERICAL -eq 0 ]] && [[ $SPECIAL -eq 0 ]] ; then
    usage
    exit 1
fi

while [ $COUNT -gt 0 ]; do
    gen_pw
done
exit 0
\$\endgroup\$
2
\$\begingroup\$

Seems all clean and proper.

But.

The function gen_pw() is a bit, how shall I put it, longwinded.

You can use variables in sed, too, so something like (in one line for easy C&P)

r1="A-Z"; r2="0-9"; r3="" ; cat /dev/urandom | tr -cd '[:graph:]' | head -c 1000| sed -e "s/[^$r1$r2$r3]//g" | head -c 10

can be done easily

If you base your program on it you can just fill the variables with the chosen characters.

r1="";
r2="";
r3="";
r4="";
if   [[ $NUMERICAL -eq 1 ]] ; then
   r1 = $nu;
elif [[ $ALPHA == l ]] || [[ $ALPHA == b ]] ; then
   r2 = $al;
elif [[ $ALPHA == u ]] || [[ $ALPHA == b ]] ; then
   r3 = $au;
elif [[ $SPECIAL -eq 1 ]] ; then
   r4 = $sc;
endif

(above is not tested, just to show what I meant) Then you run the one-liner from the top

cat /dev/urandom  | tr -cd '[:graph:]' | head -c 1000| sed -e "s/[^$r1$r2$r3$r4]//g" | head -c $STRING_LENGTH

It is a bit shorter, I think.

You still need to check if the input from the user makes sense, if the output is actually $STRING_LENGTH long (you are using a random source!) and all of the rest, but you already have most, if not all of it. And check if tr -cd '[:graph:]' doesn't let pass too much or too little.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.