Download pwgen.sh
#! /bin/bash

### pwgen.sh --- Generate a random password with /dev/urandom

## Copyright (C) 2008  Aaron S. Hawley <aaronh@localhost>

## Author: Aaron S. Hawley
## Keywords: random, unix, sysadmin

## This program is free software: you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation, either version 3 of
## the License, or (at your option) any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.

## $Id: pwgen.sh,v 1.1 2008/06/19 14:47:36 aaronh Exp $

### Commentary:

## This shell script will generate a random password using a character
## set expressed for the tr(1) command, and of the length between MIN
## and MAX.

### Usage:

## $ pwgen CHARSET MIN MAX

### Examples:

## $ pwgen [:alnum:] 0 8
## 0TfjQe

## If you don't have a fully POSIX-compliant version of tr(1), you can
## use

## $ pwgen a-zA-Z0-9 8 16
## s7RCVcOLCc

## If you need a random personal identification number for your
## account on a automated bank teller machine:

## $ pwgen 0-9 4 4
## 5120

## Note that the script does not enforce any requirements about the
## result -- for example, the existence of certain types of
## characters.

## That can easily be enforced, however by just generating a password
## that satisfies your requirements.  Here's how to use grep(1) to
## require at least one uppercase, one number and one punctuation
## character.

## $ while ( ! pwgen [:alnum:][:punct:] 2 4 \
##           | grep -e [A-Z] | grep -e [0-9] \
##           | grep -e [[:punct:]] ); do :; done
## ,8jP

### Code:

if [ -z "$1" -o -z "$2" -o -z "$3" ]; then
    echo >&2 "Usage: $0 CHARSET MIN MAX"
    exit 1;
fi

char_set="$1"
shift;

if !(echo | tr -d "${char_set}" > /dev/null 2> /dev/null ); then
    echo >&2 "Invalid character set: ${char_set}";
    exit 1;
fi

min_length="$1"
shift;

if [ ! "${min_length}" -ge 0 ]; then
    echo >&2 "Minimum length must be greater than 0: ${min_length}"
    exit 1;
fi

max_length="$1"
shift;

if [ ! "${max_length}" -ge "${min_length}" ]; then
    echo >&2 "Maximum length can't be less than minimum length: ${max_length}"
    exit 1;
fi

modulo=$(( $max_length - $min_length))

RANDOM=$$;

rand_length=$( echo $(( $RANDOM % ( $modulo + 1 ) + $min_length )) )

if [ "${rand_length}" -gt "${max_length}" \
     -o "${rand_length}" -lt "${min_length}" ]; then
    echo >&2 "Oops!  Password length not between ${min_length} and ${max_length}";
fi

## Zero-length passwords known to be insecure.
if [ "${rand_length}" == 0 ]; then
    echo "";
fi

password="$( tr -dc "${char_set}" < /dev/urandom | head -c "${rand_length}" )";

echo "${password}"

## end pwgen.sh