Skip to main content

Command-line password generator for Linux

Linux has its own cryptographic generator which can easily be harnessed by the common user to generate extremely strong passwords. This password generator can be found at /dev/random. /dev/random generates pseudorandom numbers based on the available entropy of the system (see wiki page). A derivate of /dev/random called /dev/urandom does the same thing but it is more accessible while being slightly (theoretically) less secure. So what stops us from grabbing outout from /dev/urandom using cat? An output of the content of /dev/urandom will demonstrate that it is practically gibberish with very few usable random characters. The output of

cat /dev/urandom 

should convince the user.  However, it is possible to trim out the unwanted characters and only keep alphabets, numbers, and special characters with a simple filtering by the tr command.

cat /dev/urandom | head -c 10 | tr -dc "A-Za-z0-9_!"

This command will produce the desired output but it will generate very small passwords, which are truly random but which do not meet the length criteria in most cases. Therefore, this procedure needs to be repeated a large number of times until the required length of random passwords can be generated. It is better to write a bash script which will automate this procedure.

The following bash script generates random passwords and allows the user to control the length and number of random passwords that are generated. It also allows special characters to be included or excluded with a switch (argument). Since I am always in a hurry and don't like passing too many arguments, I wrote the script so that it can accept arguments in a shorthand manner in addition to the longhand manner (Unix style) of passing arguments. Shorthand way: ./randompass.sh 10 2 1 will generate 2 random passwords of length 10 and they will all include special characters.

$ ./randompass.sh 10 2 1

ST5%l36HpG
4Tvq%Ia1UZ

Or I could use the longhand (Unix-style) way of passing the arguments: ./randompass.sh -l 10 -n 2 -s 1

$ ./randompass.sh -l 10 -n 2 -s 1

IofK1_qA%l
%5aw_YY1XZ

The result will be the same. I have included the full code below which is well commented. I also provide explanation and cite sources below the code.

#!/bin/bash

# generate random passwords using urandom CSPRNG.  

# randompass.sh v5.2 by Jones

# ---- SET INITIAL VALUES ----
length=16
num=4
special=1
usage="
Usage: ./randompass.sh [options ...]
  -l password length. DEFAULT=16
  -n number of passwords to generate. DEFAULT=4
  -s special characters. DEFAULT=1. Set to 0 to disable selecting _!%
  -h help
  
Example 1: ./randompass.sh -l 8 -n 2 -s 0
  
OR

./randompass.sh [password length] [number of passwords to generate] [include special characters] 

Example 2: ./randompass.sh 8 2 0   "

if [[ "$1" =~ ^[0-9]+$ ]]; then  
 # ---- SET INITIAL VALUES ----
 length=$1
 num=$2
 special=$3

 # Second and third argument might be empty which means $num and $special
 # will be empty at this point too. Set the values of $num and $special 
 #to the default values in case they are empty
 [ "$2" == "" ] && num=4
 [ "$3" == "" ] && special=1
else
 # If user uses arguments then use getopts.
 while getopts l:n:s:h option
 do
  case "${option}"
  in
          l) length=${OPTARG};;
          n) num=${OPTARG};;
          s) special=${OPTARG};;
          h) echo "$usage" ;;
  esac
 done
fi

# ---- CHECK ARG VALIDITY ----

# check if the first argument is an integer. If it is not then throw error. 
#=~ matches REGEXP ^[0-9]+$ which is one or more occurences of the numbers 0-9
if [[ !("$length" =~ ^[0-9]+$ )]]; then
 echo "Enter numbers only for the password length"
 echo "$usage"
 exit
fi

# check if the second argument is an integer. If it is not then throw error.
if [[ !("$num" =~ ^[0-9]+$) ]]; then
 echo "Enter numbers only for the number of passwords to generate"
 echo "$usage"
 exit
fi

# check if the third argument is an integer. If it is not then throw error. 
if [[ !("$special" =~ ^[0-1]+$) ]]; then
 echo "Enter only 0 or 1 for the third argument."
 echo "$usage"
 exit
fi


# ---- GENERATE THE PASSWORDS ----
if [[ $special == "1" ]]; then # if special chars needed: DEFAULT
 i="0"
# echo "first loop for 1"
 while [ $i -lt $num ] 
 # Keep looping till next password, which satisfies the required criteria, 
 # is successfully generated. May require many more passess for stricter 
 # requirements, usually smaller length passwords.
 do 
 pass=`tr -dc "A-Za-z0-9_!%" < /dev/urandom | head -c ${length} | xargs`

 if [ `echo $pass | grep "%" | grep "[0-9]" ` ]; then
   echo $pass
   i=$[$i+1]
 fi
 done
else  # if special chars are NOT needed
 i="0"
# echo "second loop for 0"
 while [ $i -lt $num ]
 do 
 pass=`tr -dc "A-Za-z0-9" < /dev/urandom | head -c ${length} | xargs`

 if [ `echo $pass | grep "[0-9]" ` ]; then
   echo $pass
   i=$[$i+1]
 fi
 done
fi



 

Parsing the command-line

The block of code which parses the command-line options using getopts is found at lifewire. The post at that link provides a clear explanation of what the block of code while getopts l:n:s:h option does. So I will only mention that the while loop parses through the command-line arguments and if it finds any matches to the options that the script expects, then it assigns the value passed to the option to the corresponding variables, defined in the script.

Checking validity of arguments passed

Since the script can only accept numeric arguments, their values need to be checked for non-numeric entries. If found an error message is displayed. To check if the values are numeric we can treat the value as a string and verify that every character lies between 0-9. This ensures that there are no non-numeric characters in that string which in turn implies that the string is a number. "$num" =~ ^[0-9]+$  does exactly that. =~ operator is used for REGEX matching of lvalue $num to rvalue ^[0-9]+$ (which is a REGEX that matches every character to characters between 0-9). A similar test is applied for the special variable which acts as a switch to include or exclude special characters. However, it only takes values 0 or 1. Hence its REGEX is ^0-1+$. For more information on regular expressions see this quick reference.




Comments

germacran said…
Hello Jones,

I noticed that you used getopts to parse options passed.
Unfortunately, getopt doesn't parse options longer than one character.

I wrote about alternative options on my website. If you would like to send you the link to add it as an additional resource, it would be my pleasure.

Regards,

Mokhtar

Popular posts from this blog

Fastest way to send multiple drafts from gmail

People claim that the fastest way to send multiple email drafts is to use Gmail IMAP with email client like Outlook or Evolution or Thunderbird. But I have found this is not true. Because Thunderbird and Evolution etc. email clients treats the drafts as emails still to be edited. So it is not just simple select all and hit send. Each email draft has to be opened and sent separately. That is a lot of clicks and mouse movements, wasting precious time and energy. I have a better solution which involves minimum keystrokes and mouse usage. Efficiency booster technique for sending emails. If someone is feeling adventurous and want to try it from the Gmail interface itself, here's how to do it in the fastest possible manner. It involves using the mouse once. Select the first draft. Gmail would open a new email box and put the cursor inside the box to write. Press TAB once to go the Send button. Press ENTER to send. Now Gmail sends it and the box is gone but the highlight goes to the last

LYRICS OF CHANDRABINDOO

___________________________________________________________________ SWEET HEART FROM AAR JAANI NAA(T-SERIES) -- SWEETHEART -- Pratham college-er din ta Aajo thik e mone poRey scene ta Dada didi haath dhorey siNRi tei bose poRey Aamar chokh ta ghorey bon bon bon bon Sweetheart, I am seating alone Sweetheart, for me there is none DhoNk gile chole gelo pratham maas Meye dekhlei feli deergho-shwash DhoNk gile chole gelo pratham maas Meye dekhlei othe nabhishwash Meyera bheeshan smart poRey chhoto mini-skirt Aamar e je sheet korey kon kon kon kon Sweetheart, I am seating alone Sweetheart, for me there is none Taarporey kete gelo maas chaar Fuse holo je kato future Bandhura purse khule eke oke taake tole Aamar pran ta korey chon mon chon mon Sweetheart, I am seating alone Sweetheart, for me there is none Ekdin lawn theke beriye Ek tanayaar dike taakiye Hawt korey ki je holo magaj ta ghurey gelo Taar kaaner saamne kori ghyan ghyan ghyan ghyan Sweetheart, I am seating alone Sweethea

Changing the font size of section headings in LaTex

You have several ways to do so: 1.- A direct redefinition of \section: \makeatletter \renewcommand\section{\@startsection{section}{1}{\z@}%                                   {-3.5ex \@plus -1ex \@minus -.2ex}%                                   {2.3ex \@plus.2ex}%                                   {\normalfont\large\bfseries}} \makeatother 2.- By means of the titlesec package: \usepackage{titlesec} \titleformat{\section}{\large\bfseries}{\thesection}{1em}{} 3.- By means of the sectsty package: \usepackage{sectsty} \sectionfont{\large} source : http://www.latex-community.org/forum/viewtopic.php?f=4&t=3245   Now, I would explain the titlesec package a bit more (because it seems easier to me and with more options) : To change the section fonts with this package put the following lines in the preamble - \usepackage{titlesec} \titleformat{\ section }{\ large \ bfseries }{\thesection}{1em}{} Options available are- a> Font size - \normals