Bashパスワードスクリプト用のフラグの生成

Bashパスワードスクリプト用のフラグの生成

ランダムパスワードを生成し(「フラグ」が正しい場合)、それをtxtファイルに保存するbashスクリプトを作成しました。単一のコマンドで実行できるように、スクリプトパスにエイリアスを追加しました。私はそれを「ランド」と呼びます。

私がすでに持っているのはうまくいきますが、まったくエレガントではありません。まず、フラグが今のように固定されているよりもどんな順序でも含めることができたらと思います。現在は2つだけ持っています。 -pは「セキュリティパスワード」を意味し、-sは「保存」を意味します。 "username"を表すために1つ以上の-uを追加したいのですが、現在のコード状態が気に入らないので、今は保留しています。

次のようにコマンドを実行したいと思います。

rand -FLAGS_IN_ANY_ORDER -NUMBER -FILE_NAME

以下はコードの現在のバージョンです。

#!/bin/bash

num=$#
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

if [[ "$1" = "-p" ]]; then
    cat /dev/urandom \
    | tr -dc A-Za-z0-9.-@ \
    | head -c $2 ; echo

elif [[ "$1" = "-ps" ]] && [[ "$3" = * ]]; then
    cat /dev/urandom \
    | tr -dc A-Za-z0-9.-@ \
    | head -c $2 > $DIR/$3.txt && echo >> $DIR/$3.txt

    cat $DIR/$3.txt

elif [[ "$1" = "-s" ]] && [[ "$3" = * ]]; then
    cat /dev/urandom \
    | tr -dc A-Za-z0-9 \
    | head -c $2 > $DIR/$3.txt && echo >> $DIR/$3.txt

    cat $DIR/$3.txt

else
    cat /dev/urandom \
    | tr -dc A-Za-z0-9 \
    | head -c $1 ; echo
fi

理想的には、可能な限り少ないコード行を望んでいますが、ifすべてのステートメントはそうではありません。

getopts私はこれを試しましたが、case単に動作させることはできません。これは私が今まで持っているコードの中で壊れていない唯一のバージョンです。

しかし、弁護すると、私はUNIX系システムが初めてで、数週間前からbashスクリプトの作成を始めました。

この問題をどのように解決しますか?

答え1

次のスクリプトはあなたよりも長いですが、usageそれを使用してヘルプメッセージを印刷する機能を追加し(エラー状態が検出された場合にエラーメッセージを印刷するためにも使用されます)、説明のために多くのコメントを追加したため、すべての追加の長さが発生しますしました。どのようなコードを実行し、その理由は何ですか?

オプションを解析して操作を実行するコードは、はるかに短く、単純で拡張が簡単です(たとえば、現在のオプションを使用していますが、-u username使用方法について言及していないため、そのオプションでは何もしません)。すべての特別なケースでは処理が削除され、実行するアクションと実行方法の「決定」は、if / thenテストが続くCaseステートメントで行われます。

出力ディレクトリを指定できるようにオプションも追加しました...しかし、出力ファイルに文字が-d含まれていることを確認するコードも追加したため、他のオプションを追加する方法の例外は重複します。/前に追加するだけです$outdir(名前から名前を変更します。スクリプトでは変数名を小文字として使用し、標準ユーティリティでは大文字を保持することをお勧めします)。つまり、またはを$DIR使用できます。-f /path/to/file-d /path/to -f file

ただし、このスクリプトへのフルパスを含むエイリアスを作成しないでください。これをホームディレクトリに入れるか、サブディレクトリを作成して/usr/local/bin$ PATHに追加することをお勧めします。その後、そこから必要な数のカスタムスクリプトを作成して保存できます。bin/~/bin

#!/bin/bash

usage() {
  # this function prints a usage summary, optionally printing an error message
  local ec=0

  if [ $# -ge 2 ] ; then
    # if printing an error message, the first arg is the exit code, and
    # all remaining args are the message.
    ec="$1" ; shift
    printf "%s\n\n" "$*" >&2
  fi

  cat <<EOF
Usage:
       $(basename $0) [-p | -s file | -u <user> | -d <dir> ] <[-l] length>

Generates a random password of specified length, containing only
alpha-numeric characters.

Required arguments:

  -l <length>     Length of password.  Just the length without '-l' also works.

Options:

  -p              Allow some punctuation characters in password.
  -s <file>       Save output to filename.
  -d <dir>        Output directory for the save file
  -u <user>       Username.  At present this does nothing.

  -h              This help message.
EOF

  exit $ec
}

# Initialise variables
length=''
file=''
username=''
pattern='A-Za-z0-9'
outdir=$(dirname "$0")

# -s, -l, -u and -d require an arg. -h and -p don't. The leading ':' in
# the getopts string enables 'silent' error mode (i.e. we handle
# any errors ourselves)
while getopts ':hps:l:u:d:' opt; do
  case "$opt" in 
    p) pattern='A-Za-z0-9.-@' ;;
    s) file="$OPTARG" ;;
    l) length="$OPTARG" ;;
    u) username="$OPTARG" ;;
    d) outdir="$OPTARG" ;;

    h) usage ;;
    :) usage 1 "-$OPTARG requires an argument" ;;
    ?) usage 1 "Unknown option '$opt'" ;;
  esac
done

shift $((OPTIND -1))

# Length can be specified as either `-l nnn` or just `nnn` on the cmd line
# this code exits with an error message if no length is specified.
#
# It would probably be better to give length a default value instead.
# Just change `length=''` under the Initialise variables comment above
# to whatever you want as the default, and then delete the '[ -z "$1" ]'
# line below. Remember to update the usage message - documentation should
# always match what the code does.
if [ -z "$length" ] ; then
  [ -z "$1" ] && usage 1 "Length option is required"
  length="$1"
  shift
fi

# if there are any remaining args on the line, we don't know what to do
# with them, so exit with an error message.
[ -n "$*" ] && usage 1 "Unknown arguments: '$*'"

password=$(cat /dev/urandom | tr -dc "$pattern" | head -c "$length")

# I don't know why you want an extra newline in the output, but do it anyway.
printf "$password\n\n"

if [ -n "$file" ] ; then
   # if $file doesn't have a /, pre-pend "$outdir"
   [[ $file =~ '/' ]] || file="$outdir/$file"
   printf "$password\n\n" > "$file" 
fi

ところで、すでに多くのパスワード生成プログラムが存在します。例えば普遍的な根そしてmakepasswd

私はいくつかの単語をランダムに選択し、それをランダムに生成された1〜3桁の数字および/または句読点に関連付ける独自のパスワードジェネレータを作成 pwgenするまで、16桁以上のパスワードを生成しました。すべて小文字なので、私のスクリプトはいくつかの文字をランダムに大文字で表示します。その結果、長いが覚えやすいパスワードが生成されます。ランダムな数字、句読点、および大文字は、無差別代入攻撃の検索スペースを増やし、単語辞書の予測可能性によってパスワードの強度が損なわれないようにするのに役立ちます。/usr/share/dict/words/usr/share/dict/words

例えば

$ random-password.sh 
31 surVeying % deRAngement 6 ratiocinations 51 sunDowns
$ printf '%s' '31 surVeying % deRAngement 6 ratiocinations 51 sunDowns' | wc -c
55

パスワードの長さは複雑さよりはるかに重要です。長さが10文字未満のパスワードは、最新のハードウェアを使用すると非常に短い時間で復号化できます(<= 7文字は1秒以下、8文字は数時間、10文字は数ヶ月かかります)。 )文字)。現在の技術では、55文字のパスワードは宇宙の存在中に事実上解読できません(したがって、少なくとも10年間は​​安全でなければなりません)。

問題は、パスワードが長いほど、人々はパスワードを覚えやすくする必要があるということです。私はパスワードの始まりを覚えておくだけで、パスワードを数回入力すると、意味のないフレーズの次の数字と単語が自動的に連結(したがって記憶)されることがわかりました。

答え2

これは私が一般的にオプションを実行する方法ですが、これによりスクリプトが長くなるようです...しかし、順序に関係なくオプションを受け入れます。

while (( $# )); do
    case $1 in
        -n)
            shift
            number=$1
        ;;
        -f)
            shift
            file_name=$1
        ;;
        -p)
            shift
            password=$1
            run=pass
        ;;
        -s)
            run=save
        ;;
        -u)
            shift
            username=$1
        ;;
        *)
            thing=$1
            save=what
        ;;
        shift
    esac
done
case $save in
    save)
        cat /dev/urandom \
            | tr -dc A-Za-z0-9.-@ \
            | head -c "$number" > "${DIR}/${file_name}.txt" && echo >> "${DIR}/${file_name}.txt"
        cat "${DIR}/${file_name}.txt"
    ;;
    pass)
        cat /dev/urandom \
        | tr -dc A-Za-z0-9.-@ \
        | head -c "$number" ; echo
    ;;
    *)
        cat /dev/urandom \
        | tr -dc A-Za-z0-9 \
        | head -c "$thing" ; echo
    ;;
esac

whileオプションがある限り、ループが実行されます。shift渡した各引数を繰り返し、Caseステートメントを介して実行して正しく処理できるように、次のオプションに進みます。

答え3

以下はスクリプトのバリエーションです。

#!/bin/bash

opt_l=8                 # password length is 8 by default
opt_o=/dev/stdout       # write to standard output by default
opt_s=0                 # insecure password by default
opt_u=                  # no default username

while getopts 'l:o:su:' opt; do
    case "$opt" in
        l) opt_l=$OPTARG ;;
        o) opt_o=$OPTARG ;;
        s) opt_s=1       ;;
        u) opt_u=$OPTARG ;;
        *) echo 'Error parsing options' >&2
           exit 1
   esac
done

args=( "$opt_l" 1 )

if (( opt_s )); then
    args=( -s "${args[@]}" )
fi

if [ -n "$opt_u" ]; then
    printf '%s: %s\n' "$opt_u" "$( pwgen "${args[@]}" )" >"$opt_o"
else
    pwgen "${args[@]}" >"$opt_o"
fi

標準ツールの使用とより一貫性があるように、一部のフラグの名前を変更しました。サポートされていないフラグが見つかった場合は、エラーメッセージを印刷するのではなく、ユーザーにいくつかの使用情報を出力できます。

ここで主にgetopts使用されます。使用中のオプションの文字列として仕様を使用します。文字列の:前のオプション文字が引数を取ることを示します(他のオプションはブール値です)。

whileループ内のフラグが引数を取る場合、willはオプション$opt、willはオプション引数です。$OPTARG

これは通常shift "$(( OPTIND - 1 ))"ループの後でも行われますが、スクリプトはオプションとその引数を除いてコマンドラインで他のオペランドを受け入れないため、必要ありません。これにより、追加のオペランドを次shiftのように使用できます。$1$2

このスクリプトは次のように使用できます。

$ ./script.sh -l 4         # generates password of length 4 in the terminal
$ ./script.sh -l 4 -o file # generates password of length 4 and saves to "file"
$ ./script.sh -u bob       # generates password of length 8, prepends with username "bob"
$ ./script.sh -s -u bob    # generates more random password of length 8, prepends with username "bob"

-s -u bob-ubob -s-subob、とと同じです-u alice -s -u bob(後者のオプションは前のオプションよりも優先されます)。

pwgenこれを削除し、他のパスワードジェネレータを使用するのは非常に簡単です。これを簡単にするために、パスワードジェネレータを独自の機能に入れることができます。つまり、ジェネレータを交換するときにデフォルトのスクリプトを変更する必要はありません。

#!/bin/bash

passgen () {
    local args=( "$opt_l" 1 )

    if (( opt_s )); then
        args=( -s "${args[@]}" )
    fi

    pwgen "${args[@]}"
}

opt_l=8                 # password length is 8 by default
opt_o=/dev/stdout       # write to standard output by default
opt_s=0                 # insecure password by default
opt_u=                  # no default username

while getopts 'l:o:su:' opt; do
    case "$opt" in
        l) opt_l=$OPTARG ;;
        o) opt_o=$OPTARG ;;
        s) opt_s=1       ;;
        u) opt_u=$OPTARG ;;
        *) echo 'Error parsing options' >&2
           exit 1
   esac
done

if [ -n "$opt_u" ]; then
    printf '%s: %s\n' "$opt_u" "$( passgen )" >"$opt_o"
else
    passgen >"$opt_o"
fi

たとえば、以下はジェネレータです。

passgen () {
    local source=/dev/urandom
    local chars='A-Za-z0-9'

    if (( opt_s )); then
        chars="$chars"'.-@'
    fi

    tr -dc "$chars" <"$source" | head -c "$opt_l"
}

...次は単語を使用してパスワードを生成します/usr/share/dict/words(単語数は-lオプションから取得されます)。このオプションを使用すると、-s最後の単語の代わりに最後にランダムな数字が追加されます。

passgen () {
    local dict=/usr/share/dict/words
    local numwords=$opt_l

    if (( opt_s )); then
        numwords=$(( numwords - 1 ))
    fi

    {
        shuf -n "$numwords" "$dict"

        if (( opt_s )); then
            printf '%d\n' "$RANDOM"
        fi
    } | tr '\n' ' ' | sed 's/ $//'
}

最後passgenの機能を実行するには、次のコマンドを実行します。

./script.sh -s -u bob -l 3

次のようなものを作成できます。

bob: cassis befluster 22625

関連情報