Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Random Password Generator Bash

Tags:

bash

passwords

I am trying to make a password generator that generates a password between 8-16 characters. It also has to contain a digit and lower and uppercase letters, as well as only one special character that is randomly placed in the string when being generated.

At the moment, my code will sometimes give me a password that does not meet the requirements eg. AOKOKKK@1 (no lowercase letters) In addition, I also need help to make it so that the special symbol only appears once in the password. How can I fix this?

Here's my code:

length=$[ 8 +$[RANDOM % 16]]

char=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ! @ \# $ % ^ \&)

max=${#char[*]}
for ((i = 1; i <= $length ; i++))do

let rand=${RANDOM}%${max}
password="${password}${char[$rand]}"
done
echo $password
like image 781
Jeremy Avatar asked Oct 30 '14 23:10

Jeremy


2 Answers

This guarantees one and only one special characters, as well as at least one of digits, lower case, and upper case. To place those characters randomly within the password, sort -R is used to scramble the order of characters before the password is printed:

#!/bin/bash
choose() { echo ${1:RANDOM%${#1}:1}; }

{
    choose '!@#$%^\&'
    choose '0123456789'
    choose 'abcdefghijklmnopqrstuvwxyz'
    choose 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    for i in $( seq 1 $(( 4 + RANDOM % 8 )) )
    do
        choose '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    done
} | sort -R | tr -d '\n'
echo ""

How it works

  1. We define a convenience function:

    choose() { echo ${1:RANDOM%${#1}:1}; }
    

    choose takes one argument, a string, and randomly selects a character from that string.

  2. We print one character per line of one of each of the required types of characters:

    choose '!@#$%^\&'
    choose '0123456789'
    choose 'abcdefghijklmnopqrstuvwxyz'
    choose 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    
  3. We print the remaining number of characters needed, one character per line. These characters are selected randomly from digits, lower case, and upper case:

    for i in $( seq 1 $(( 4 + RANDOM % 8 )) )
    do
        choose '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    done
    
  4. From the above, the special character is always first and the digit second, etc. We need to randomize. We do that with sort -R:

    sort -R | tr -d '\n'
    

    tr -d '\n' serves to removes the newlines from between the characters, resulting in a string of characters on one line.

Improvement

In the comments, JonathanLeffer shows that the output of sort -R is not fully random. sort -R sorts on a hash of each line. Apparently a random seed is added but each line receives the same seed. To get around this, the version below provides its own seed on each line. This is done by a change to the choose function. At the end, awk is used to remove those numbers and display the complete password:

#!/bin/bash
choose() { echo ${1:RANDOM%${#1}:1} $RANDOM; }

{
    choose '!@#$%^\&'
    choose '0123456789'
    choose 'abcdefghijklmnopqrstuvwxyz'
    choose 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    for i in $( seq 1 $(( 4 + RANDOM % 8 )) )
    do
        choose '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    done

} | sort -R | awk '{printf "%s",$1}'
echo ""

In awk, each line is divided into fields. The first field is the randomly chosen letter while the second is the random number that we added to seed the hash. The statement printf "%s",$1 prints the first field (with no trailing whitespace) and ignores the second field. The end result is the password that we want.

Capturing the password to a variable

To capture the output to a variable, we use command substitution:

choose() { echo ${1:RANDOM%${#1}:1} $RANDOM; }
pass="$({ choose '!@#$%^\&'
  choose '0123456789'
  choose 'abcdefghijklmnopqrstuvwxyz'
  choose 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  for i in $( seq 1 $(( 4 + RANDOM % 8 )) )
     do
        choose '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
     done
 } | sort -R | awk '{printf "%s",$1}')"

Command substitution, denoted by $(...), runs the commands in the parens and captures their stdout.

like image 96
John1024 Avatar answered Sep 26 '22 16:09

John1024


pwgen is installed on some linux distros (notably Ubuntu) by default and has a windows version as well. Why not use that?

like image 44
whereswalden Avatar answered Sep 25 '22 16:09

whereswalden