如何从shell脚本获得密码而不回显

我有一个脚本,自动需要访问密码保护系统的进程。通过一个接受用户密码作为参数的命令行程序访问系统。

我想提示用户输入密码,将其分配给一个shell变量,然后使用该变量构造访问程序的命令行(当然,这将产生我将处理的流输出)。

我在Bourne/Bash中是一个相当称职的shell程序员,但我不知道如何接受用户输入而不让它回显到终端(或者可能让它使用'*'字符回显)。

有人能帮忙吗?

535308 次浏览

使用stty关闭echo,然后再次打开。

一个POSIX兼容的答案。注意使用了/bin/sh而不是/bin/bash。(它与bash一起工作,但不使用需要 bash。)

#!/bin/sh
stty -echo
printf "Password: "
read PASSWORD
stty echo
printf "\n"

这里有另一种方法:

#!/bin/bash
# Read Password
echo -n Password:
read -s password
echo
# Run Command
echo $password

read -s将为你关闭echo。只需将最后一行的echo替换为要运行的命令。

在一些shell(例如bash)中,read支持-p prompt-string,这将允许echo和read命令合并。

read -s -p "Password: " password

一个衬套:

read -s -p "Password: " password

在Linux(和cygwin)下,这种形式在bash和sh中工作。不过,它可能不是标准的Unix sh。

更多信息和选项,在bash中输入“help read”。

$ help read
read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
Read a line from the standard input and split it into fields.
...
-p prompt output the string PROMPT without a trailing newline before
attempting to read
...
-s                do not echo input coming from a terminal

read-s选项在POSIX标准中没有定义。看到# EYZ3。我想要一些可以工作于任何POSIX shell的东西,所以我写了一个小函数,使用stty禁用echo。

#!/bin/sh


# Read secret string
read_secret()
{
# Disable echo.
stty -echo


# Set up trap to ensure echo is enabled before exiting if the script
# is terminated while echo is disabled.
trap 'stty echo' EXIT


# Read secret.
read "$@"


# Enable echo.
stty echo
trap - EXIT


# Print a newline because the newline entered by the user after
# entering the passcode is not echoed. This ensures that the
# next line of output begins at a new line.
echo
}

这个函数的行为非常类似于read命令。下面是read的简单用法和read_secret的类似用法。read_secret的输入显示为空,因为它没有回显到终端。

[susam@cube ~]$ read a b c
foo \bar baz \qux
[susam@cube ~]$ echo a=$a b=$b c=$c
a=foo b=bar c=baz qux
[susam@cube ~]$ unset a b c
[susam@cube ~]$ read_secret a b c


[susam@cube ~]$ echo a=$a b=$b c=$c
a=foo b=bar c=baz qux
[susam@cube ~]$ unset a b c

下面是另一个使用-r选项来保留输入中的反斜杠。这是因为上面定义的read_secret函数将它接收到的所有参数传递给read命令。

[susam@cube ~]$ read -r a b c
foo \bar baz \qux
[susam@cube ~]$ echo a=$a b=$b c=$c
a=foo b=\bar c=baz \qux
[susam@cube ~]$ unset a b c
[susam@cube ~]$ read_secret -r a b c


[susam@cube ~]$ echo a=$a b=$b c=$c
a=foo b=\bar c=baz \qux
[susam@cube ~]$ unset a b c

最后,下面是一个示例,演示如何使用read_secret函数以符合POSIX的方式读取密码。

printf "Password: "
read_secret password
# Do something with $password here ...

我发现askpass命令很有用

password=$(/lib/cryptsetup/askpass "Give a password")

每个输入字符替换为*。看到的: 给出密码****

你也可以在不设置当前shell变量的情况下提示输入密码,如下所示:

$(read -s;echo $REPLY)

例如:

my-command --set password=$(read -sp "Password: ";echo $REPLY)

你可以用换行符添加这些提示值,这样做:

my-command --set user=$(read -sp "`echo $'\n '`User: ";echo $REPLY) --set password=$(read -sp "`echo $'\n '`Password: ";echo $REPLY)

这个链接有助于定义, *如何从使用中读取密码而不将其返回到终端 *如何将每个字符替换为* -character

https://www.tutorialkart.com/bash-shell-scripting/bash-read-username-and-password/

对于任何需要提示输入密码的人,您可能对使用encpass.sh感兴趣。这是我为类似的目的而编写的脚本,即在运行时捕获秘密,然后为后续场合加密它。后续运行不会提示输入密码,因为它将只使用来自磁盘的加密值。

它将加密的密码存储在用户主目录下的一个隐藏文件夹中,或者存储在可以通过环境变量ENCPASS_HOME_DIR定义的自定义文件夹中。它被设计为POSIX兼容,并拥有MIT许可证,因此它甚至可以在企业环境中使用。我的公司Plyint有限责任公司维护脚本,偶尔发布更新。如果您发现问题,也欢迎Pull请求。:)

要在脚本中使用它,只需在脚本中获取encpass.sh并调用get_secret函数。为了便于查看,我在下面包含了一个脚本副本。

#!/bin/sh
################################################################################
# Copyright (c) 2020 Plyint, LLC <contact@plyint.com>. All Rights Reserved.
# This file is licensed under the MIT License (MIT).
# Please see LICENSE.txt for more information.
#
# DESCRIPTION:
# This script allows a user to encrypt a password (or any other secret) at
# runtime and then use it, decrypted, within a script.  This prevents shoulder
# surfing passwords and avoids storing the password in plain text, which could
# inadvertently be sent to or discovered by an individual at a later date.
#
# This script generates an AES 256 bit symmetric key for each script (or user-
# defined bucket) that stores secrets.  This key will then be used to encrypt
# all secrets for that script or bucket.  encpass.sh sets up a directory
# (.encpass) under the user's home directory where keys and secrets will be
# stored.
#
# For further details, see README.md or run "./encpass ?" from the command line.
#
################################################################################


encpass_checks() {
if [ -n "$ENCPASS_CHECKS" ]; then
return
fi


if [ ! -x "$(command -v openssl)" ]; then
echo "Error: OpenSSL is not installed or not accessible in the current path." \
"Please install it and try again." >&2
exit 1
fi


if [ -z "$ENCPASS_HOME_DIR" ]; then
ENCPASS_HOME_DIR=$(encpass_get_abs_filename ~)/.encpass
fi


if [ ! -d "$ENCPASS_HOME_DIR" ]; then
mkdir -m 700 "$ENCPASS_HOME_DIR"
mkdir -m 700 "$ENCPASS_HOME_DIR/keys"
mkdir -m 700 "$ENCPASS_HOME_DIR/secrets"
fi


if [ "$(basename "$0")" != "encpass.sh" ]; then
encpass_include_init "$1" "$2"
fi


ENCPASS_CHECKS=1
}


# Initializations performed when the script is included by another script
encpass_include_init() {
if [ -n "$1" ] && [ -n "$2" ]; then
ENCPASS_BUCKET=$1
ENCPASS_SECRET_NAME=$2
elif [ -n "$1" ]; then
ENCPASS_BUCKET=$(basename "$0")
ENCPASS_SECRET_NAME=$1
else
ENCPASS_BUCKET=$(basename "$0")
ENCPASS_SECRET_NAME="password"
fi
}


encpass_generate_private_key() {
ENCPASS_KEY_DIR="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET"


if [ ! -d "$ENCPASS_KEY_DIR" ]; then
mkdir -m 700 "$ENCPASS_KEY_DIR"
fi


if [ ! -f "$ENCPASS_KEY_DIR/private.key" ]; then
(umask 0377 && printf "%s" "$(openssl rand -hex 32)" >"$ENCPASS_KEY_DIR/private.key")
fi
}


encpass_get_private_key_abs_name() {
ENCPASS_PRIVATE_KEY_ABS_NAME="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key"


if [ "$1" != "nogenerate" ]; then
if [ ! -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then
encpass_generate_private_key
fi
fi
}


encpass_get_secret_abs_name() {
ENCPASS_SECRET_ABS_NAME="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET/$ENCPASS_SECRET_NAME.enc"


if [ "$3" != "nocreate" ]; then
if [ ! -f "$ENCPASS_SECRET_ABS_NAME" ]; then
set_secret "$1" "$2"
fi
fi
}


get_secret() {
encpass_checks "$1" "$2"
encpass_get_private_key_abs_name
encpass_get_secret_abs_name "$1" "$2"
encpass_decrypt_secret
}


set_secret() {
encpass_checks "$1" "$2"


if [ "$3" != "reuse" ] || { [ -z "$ENCPASS_SECRET_INPUT" ] && [ -z "$ENCPASS_CSECRET_INPUT" ]; }; then
echo "Enter $ENCPASS_SECRET_NAME:" >&2
stty -echo
read -r ENCPASS_SECRET_INPUT
stty echo
echo "Confirm $ENCPASS_SECRET_NAME:" >&2
stty -echo
read -r ENCPASS_CSECRET_INPUT
stty echo
fi


if [ "$ENCPASS_SECRET_INPUT" = "$ENCPASS_CSECRET_INPUT" ]; then
encpass_get_private_key_abs_name
ENCPASS_SECRET_DIR="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET"


if [ ! -d "$ENCPASS_SECRET_DIR" ]; then
mkdir -m 700 "$ENCPASS_SECRET_DIR"
fi


printf "%s" "$(openssl rand -hex 16)" >"$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc"


ENCPASS_OPENSSL_IV="$(cat "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc")"


echo "$ENCPASS_SECRET_INPUT" | openssl enc -aes-256-cbc -e -a -iv \
"$ENCPASS_OPENSSL_IV" -K \
"$(cat "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key")" 1>> \
"$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc"
else
echo "Error: secrets do not match.  Please try again." >&2
exit 1
fi
}


encpass_get_abs_filename() {
# $1 : relative filename
filename="$1"
parentdir="$(dirname "${filename}")"


if [ -d "${filename}" ]; then
cd "${filename}" && pwd
elif [ -d "${parentdir}" ]; then
echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")"
fi
}


encpass_decrypt_secret() {
if [ -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then
ENCPASS_DECRYPT_RESULT="$(dd if="$ENCPASS_SECRET_ABS_NAME" ibs=1 skip=32 2> /dev/null | openssl enc -aes-256-cbc \
-d -a -iv "$(head -c 32 "$ENCPASS_SECRET_ABS_NAME")" -K "$(cat "$ENCPASS_PRIVATE_KEY_ABS_NAME")" 2> /dev/null)"
if [ ! -z "$ENCPASS_DECRYPT_RESULT" ]; then
echo "$ENCPASS_DECRYPT_RESULT"
else
# If a failed unlock command occurred and the user tries to show the secret
# Present either locked or decrypt command
if [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then
echo "**Locked**"
else
# The locked file wasn't present as expected.  Let's display a failure
echo "Error: Failed to decrypt"
fi
fi
elif [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then
echo "**Locked**"
else
echo "Error: Unable to decrypt. The key file \"$ENCPASS_PRIVATE_KEY_ABS_NAME\" is not present."
fi
}




##########################################################
# COMMAND LINE MANAGEMENT SUPPORT
# -------------------------------
# If you don't need to manage the secrets for the scripts
# with encpass.sh you can delete all code below this point
# in order to significantly reduce the size of encpass.sh.
# This is useful if you want to bundle encpass.sh with
# your existing scripts and just need the retrieval
# functions.
##########################################################


encpass_show_secret() {
encpass_checks
ENCPASS_BUCKET=$1


encpass_get_private_key_abs_name "nogenerate"


if [ ! -z "$2" ]; then
ENCPASS_SECRET_NAME=$2
encpass_get_secret_abs_name "$1" "$2" "nocreate"
if [ -z "$ENCPASS_SECRET_ABS_NAME" ]; then
echo "No secret named $2 found for bucket $1."
exit 1
fi


encpass_decrypt_secret
else
ENCPASS_FILE_LIST=$(ls -1 "$ENCPASS_HOME_DIR"/secrets/"$1")
for ENCPASS_F in $ENCPASS_FILE_LIST; do
ENCPASS_SECRET_NAME=$(basename "$ENCPASS_F" .enc)


encpass_get_secret_abs_name "$1" "$ENCPASS_SECRET_NAME" "nocreate"
if [ -z "$ENCPASS_SECRET_ABS_NAME" ]; then
echo "No secret named $ENCPASS_SECRET_NAME found for bucket $1."
exit 1
fi


echo "$ENCPASS_SECRET_NAME = $(encpass_decrypt_secret)"
done
fi
}


encpass_getche() {
old=$(stty -g)
stty raw min 1 time 0
printf '%s' "$(dd bs=1 count=1 2>/dev/null)"
stty "$old"
}


encpass_remove() {
if [ ! -n "$ENCPASS_FORCE_REMOVE" ]; then
if [ ! -z "$ENCPASS_SECRET" ]; then
printf "Are you sure you want to remove the secret \"%s\" from bucket \"%s\"? [y/N]" "$ENCPASS_SECRET" "$ENCPASS_BUCKET"
else
printf "Are you sure you want to remove the bucket \"%s?\" [y/N]" "$ENCPASS_BUCKET"
fi


ENCPASS_CONFIRM="$(encpass_getche)"
printf "\n"
if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then
exit 0
fi
fi


if [ ! -z "$ENCPASS_SECRET" ]; then
rm -f "$1"
printf "Secret \"%s\" removed from bucket \"%s\".\n" "$ENCPASS_SECRET" "$ENCPASS_BUCKET"
else
rm -Rf "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET"
rm -Rf "$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET"
printf "Bucket \"%s\" removed.\n" "$ENCPASS_BUCKET"
fi
}


encpass_save_err() {
if read -r x; then
{ printf "%s\n" "$x"; cat; } > "$1"
elif [ "$x" != "" ]; then
printf "%s" "$x" > "$1"
fi
}


encpass_help() {
less << EOF
NAME:
encpass.sh - Use encrypted passwords in shell scripts


DESCRIPTION:
A lightweight solution for using encrypted passwords in shell scripts
using OpenSSL. It allows a user to encrypt a password (or any other secret)
at runtime and then use it, decrypted, within a script. This prevents
shoulder surfing passwords and avoids storing the password in plain text,
within a script, which could inadvertently be sent to or discovered by an
individual at a later date.


This script generates an AES 256 bit symmetric key for each script
(or user-defined bucket) that stores secrets. This key will then be used
to encrypt all secrets for that script or bucket.


Subsequent calls to retrieve a secret will not prompt for a secret to be
entered as the file with the encrypted value already exists.


Note: By default, encpass.sh sets up a directory (.encpass) under the
user's home directory where keys and secrets will be stored.  This directory
can be overridden by setting the environment variable ENCPASS_HOME_DIR to a
directory of your choice.


~/.encpass (or the directory specified by ENCPASS_HOME_DIR) will contain
the following subdirectories:
- keys (Holds the private key for each script/bucket)
- secrets (Holds the secrets stored for each script/bucket)


USAGE:
To use the encpass.sh script in an existing shell script, source the script
and then call the get_secret function.


Example:


#!/bin/sh
. encpass.sh
password=\$(get_secret)


When no arguments are passed to the get_secret function,
then the bucket name is set to the name of the script and
the secret name is set to "password".


There are 2 other ways to call get_secret:


Specify the secret name:
Ex: \$(get_secret user)
- bucket name = <script name>
- secret name = "user"


Specify both the secret name and bucket name:
Ex: \$(get_secret personal user)
- bucket name = "personal"
- secret name = "user"


encpass.sh also provides a command line interface to manage the secrets.
To invoke a command, pass it as an argument to encpass.sh from the shell.


$ encpass.sh [COMMAND]


See the COMMANDS section below for a list of available commands.  Wildcard
handling is implemented for secret and bucket names.  This enables
performing operations like adding/removing a secret to/from multiple buckets
at once.


COMMANDS:
add [-f] <bucket> <secret>
Add a secret to the specified bucket.  The bucket will be created
if it does not already exist. If a secret with the same name already
exists for the specified bucket, then the user will be prompted to
confirm overwriting the value.  If the -f option is passed, then the
add operation will perform a forceful overwrite of the value. (i.e. no
prompt)


list|ls [<bucket>]
Display the names of the secrets held in the bucket.  If no bucket
is specified, then the names of all existing buckets will be
displayed.


lock
Locks all keys used by encpass.sh using a password.  The user
will be prompted to enter a password and confirm it.  A user
should take care to securely store the password.  If the password
is lost then keys can not be unlocked.  When keys are locked,
secrets can not be retrieved. (e.g. the output of the values
in the "show" command will be encrypted/garbage)


remove|rm [-f] <bucket> [<secret>]
Remove a secret from the specified bucket.  If only a bucket is
specified then the entire bucket (i.e. all secrets and keys) will
be removed.  By default the user is asked to confirm the removal of
the secret or the bucket.  If the -f option is passed then a
forceful removal will be performed.  (i.e. no prompt)


show [<bucket>] [<secret>]
Show the unencrypted value of the secret from the specified bucket.
If no secret is specified then all secrets for the bucket are displayed.


update <bucket> <secret>
Updates a secret in the specified bucket.  This command is similar
to using an "add -f" command, but it has a safety check to only
proceed if the specified secret exists.  If the secret, does not
already exist, then an error will be reported. There is no forceable
update implemented.  Use "add -f" for any required forceable update
scenarios.


unlock
Unlocks all the keys for encpass.sh.  The user will be prompted to
enter the password and confirm it.


dir
Prints out the current value of the ENCPASS_HOME_DIR environment variable.


help|--help|usage|--usage|?
Display this help message.
EOF
}


# Subcommands for cli support
case "$1" in
add )
shift
while getopts ":f" ENCPASS_OPTS; do
case "$ENCPASS_OPTS" in
f ) ENCPASS_FORCE_ADD=1;;
esac
done


encpass_checks


if [ -n "$ENCPASS_FORCE_ADD" ]; then
shift $((OPTIND-1))
fi


if [ ! -z "$1" ] && [ ! -z "$2" ]; then
# Allow globbing
# shellcheck disable=SC2027,SC2086
ENCPASS_ADD_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
if [ -z "$ENCPASS_ADD_LIST" ]; then
ENCPASS_ADD_LIST="$1"
fi


for ENCPASS_ADD_F in $ENCPASS_ADD_LIST; do
ENCPASS_ADD_DIR="$(basename "$ENCPASS_ADD_F")"
ENCPASS_BUCKET="$ENCPASS_ADD_DIR"
if [ ! -n "$ENCPASS_FORCE_ADD" ] && [ -f "$ENCPASS_ADD_F/$2.enc" ]; then
echo "Warning: A secret with the name \"$2\" already exists for bucket $ENCPASS_BUCKET."
echo "Would you like to overwrite the value? [y/N]"


ENCPASS_CONFIRM="$(encpass_getche)"
if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then
continue
fi
fi


ENCPASS_SECRET_NAME="$2"
echo "Adding secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..."
set_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET_NAME" "reuse"
done
else
echo "Error: A bucket name and secret name must be provided when adding a secret."
exit 1
fi
;;
update )
shift


encpass_checks
if [ ! -z "$1" ] && [ ! -z "$2" ]; then


ENCPASS_SECRET_NAME="$2"
# Allow globbing
# shellcheck disable=SC2027,SC2086
ENCPASS_UPDATE_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"


for ENCPASS_UPDATE_F in $ENCPASS_UPDATE_LIST; do
# Allow globbing
# shellcheck disable=SC2027,SC2086
if [ -f "$ENCPASS_UPDATE_F/"$2".enc" ]; then
ENCPASS_UPDATE_DIR="$(basename "$ENCPASS_UPDATE_F")"
ENCPASS_BUCKET="$ENCPASS_UPDATE_DIR"
echo "Updating secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..."
set_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET_NAME" "reuse"
else
echo "Error: A secret with the name \"$2\" does not exist for bucket $1."
exit 1
fi
done
else
echo "Error: A bucket name and secret name must be provided when updating a secret."
exit 1
fi
;;
rm|remove )
shift
encpass_checks


while getopts ":f" ENCPASS_OPTS; do
case "$ENCPASS_OPTS" in
f ) ENCPASS_FORCE_REMOVE=1;;
esac
done


if [ -n "$ENCPASS_FORCE_REMOVE" ]; then
shift $((OPTIND-1))
fi


if [ -z "$1" ]; then
echo "Error: A bucket must be specified for removal."
fi


# Allow globbing
# shellcheck disable=SC2027,SC2086
ENCPASS_REMOVE_BKT_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
if [ ! -z "$ENCPASS_REMOVE_BKT_LIST" ]; then
for ENCPASS_REMOVE_B in $ENCPASS_REMOVE_BKT_LIST; do


ENCPASS_BUCKET="$(basename "$ENCPASS_REMOVE_B")"
if [ ! -z "$2" ]; then
# Removing secrets for a specified bucket
# Allow globbing
# shellcheck disable=SC2027,SC2086
ENCPASS_REMOVE_LIST="$(ls -1p "$ENCPASS_REMOVE_B/"$2".enc" 2>/dev/null)"


if [ -z "$ENCPASS_REMOVE_LIST" ]; then
echo "Error: No secrets found for $2 in bucket $ENCPASS_BUCKET."
exit 1
fi


for ENCPASS_REMOVE_F in $ENCPASS_REMOVE_LIST; do
ENCPASS_SECRET="$2"
encpass_remove "$ENCPASS_REMOVE_F"
done
else
# Removing a specified bucket
encpass_remove
fi


done
else
echo "Error: The bucket named $1 does not exist."
exit 1
fi
;;
show )
shift
encpass_checks
if [ -z "$1" ]; then
ENCPASS_SHOW_DIR="*"
else
ENCPASS_SHOW_DIR=$1
fi


if [ ! -z "$2" ]; then
# Allow globbing
# shellcheck disable=SC2027,SC2086
if [ -f "$(encpass_get_abs_filename "$ENCPASS_HOME_DIR/secrets/$ENCPASS_SHOW_DIR/"$2".enc")" ]; then
encpass_show_secret "$ENCPASS_SHOW_DIR" "$2"
fi
else
# Allow globbing
# shellcheck disable=SC2027,SC2086
ENCPASS_SHOW_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$ENCPASS_SHOW_DIR"" 2>/dev/null)"


if [ -z "$ENCPASS_SHOW_LIST" ]; then
if [ "$ENCPASS_SHOW_DIR" = "*" ]; then
echo "Error: No buckets exist."
else
echo "Error: Bucket $1 does not exist."
fi
exit 1
fi


for ENCPASS_SHOW_F in $ENCPASS_SHOW_LIST; do
ENCPASS_SHOW_DIR="$(basename "$ENCPASS_SHOW_F")"
echo "$ENCPASS_SHOW_DIR:"
encpass_show_secret "$ENCPASS_SHOW_DIR"
echo " "
done
fi
;;
ls|list )
shift
encpass_checks
if [ ! -z "$1" ]; then
# Allow globbing
# shellcheck disable=SC2027,SC2086
ENCPASS_FILE_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"


if [ -z "$ENCPASS_FILE_LIST" ]; then
# Allow globbing
# shellcheck disable=SC2027,SC2086
ENCPASS_DIR_EXISTS="$(ls -d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
if [ ! -z "$ENCPASS_DIR_EXISTS" ]; then
echo "Bucket $1 is empty."
else
echo "Error: Bucket $1 does not exist."
fi
exit 1
fi


ENCPASS_NL=""
for ENCPASS_F in $ENCPASS_FILE_LIST; do
if [ -d "${ENCPASS_F%:}" ]; then
printf "$ENCPASS_NL%s\n" "$(basename "$ENCPASS_F")"
ENCPASS_NL="\n"
else
printf "%s\n" "$(basename "$ENCPASS_F" .enc)"
fi
done
else
# Allow globbing
# shellcheck disable=SC2027,SC2086
ENCPASS_BUCKET_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
for ENCPASS_C in $ENCPASS_BUCKET_LIST; do
if [ -d "${ENCPASS_C%:}" ]; then
printf "\n%s" "\n$(basename "$ENCPASS_C")"
else
basename "$ENCPASS_C" .enc
fi
done
fi
;;
lock )
shift
encpass_checks


echo "************************!!!WARNING!!!*************************" >&2
echo "* You are about to lock your keys with a password.           *" >&2
echo "* You will not be able to use your secrets again until you   *" >&2
echo "* unlock the keys with the same password. It is important    *" >&2
echo "* that you securely store the password, so you can recall it *" >&2
echo "* in the future.  If you forget your password you will no    *" >&2
echo "* longer be able to access your secrets.                     *" >&2
echo "************************!!!WARNING!!!*************************" >&2


printf "\n%s\n" "About to lock keys held in directory $ENCPASS_HOME_DIR/keys/"


printf "\nEnter Password to lock keys:" >&2
stty -echo
read -r ENCPASS_KEY_PASS
printf "\nConfirm Password:" >&2
read -r ENCPASS_CKEY_PASS
printf "\n"
stty echo


if [ -z "$ENCPASS_KEY_PASS" ]; then
echo "Error: You must supply a password value."
exit 1
fi


if [ "$ENCPASS_KEY_PASS" = "$ENCPASS_CKEY_PASS" ]; then
ENCPASS_NUM_KEYS_LOCKED=0
ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)"
for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do


if [ -d "${ENCPASS_KEY_F%:}" ]; then
ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")"
ENCPASS_KEY_VALUE=""
if [ -f "$ENCPASS_KEY_F/private.key" ]; then
ENCPASS_KEY_VALUE="$(cat "$ENCPASS_KEY_F/private.key")"
if [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then
echo "Locking key $ENCPASS_KEY_NAME..."
else
echo "Error: The key $ENCPASS_KEY_NAME appears to have been previously locked."
echo "       The current key file may hold a bad value. Exiting to avoid encrypting"
echo "       a bad value and overwriting the lock file."
exit 1
fi
else
echo "Error: Private key file ${ENCPASS_KEY_F}private.key missing for bucket $ENCPASS_KEY_NAME."
exit 1
fi
if [ ! -z "$ENCPASS_KEY_VALUE" ]; then
openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -salt -in "$ENCPASS_KEY_F/private.key" -out "$ENCPASS_KEY_F/private.lock" -k "$ENCPASS_KEY_PASS"
if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then
# Both the key and lock file exist.  We can remove the key file now
rm -f "$ENCPASS_KEY_F/private.key"
echo "Locked key $ENCPASS_KEY_NAME."
ENCPASS_NUM_KEYS_LOCKED=$(( ENCPASS_NUM_KEYS_LOCKED + 1 ))
else
echo "Error: The key fle and/or lock file were not found as expected for key $ENCPASS_KEY_NAME."
fi
else
echo "Error: No key value found for the $ENCPASS_KEY_NAME key."
exit 1
fi
fi
done
echo "Locked $ENCPASS_NUM_KEYS_LOCKED keys."
else
echo "Error: Passwords do not match."
fi
;;
unlock )
shift
encpass_checks


printf "%s\n" "About to unlock keys held in the $ENCPASS_HOME_DIR/keys/ directory."


printf "\nEnter Password to unlock keys: " >&2
stty -echo
read -r ENCPASS_KEY_PASS
printf "\n"
stty echo


if [ ! -z "$ENCPASS_KEY_PASS" ]; then
ENCPASS_NUM_KEYS_UNLOCKED=0
ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)"
for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do


if [ -d "${ENCPASS_KEY_F%:}" ]; then
ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")"
echo "Unlocking key $ENCPASS_KEY_NAME..."
if [ -f "$ENCPASS_KEY_F/private.key" ] && [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then
echo "Error: Key $ENCPASS_KEY_NAME appears to be unlocked already."
exit 1
fi


if [ -f "$ENCPASS_KEY_F/private.lock" ]; then
# Remove the failed file in case previous decryption attempts were unsuccessful
rm -f "$ENCPASS_KEY_F/failed" 2>/dev/null


# Decrypt key. Log any failure to the "failed" file.
openssl enc -aes-256-cbc -d -pbkdf2 -iter 10000 -salt \
-in "$ENCPASS_KEY_F/private.lock" -out "$ENCPASS_KEY_F/private.key" \
-k "$ENCPASS_KEY_PASS" 2>&1 | encpass_save_err "$ENCPASS_KEY_F/failed"


if [ ! -f "$ENCPASS_KEY_F/failed" ]; then
# No failure has occurred.
if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then
# Both the key and lock file exist.  We can remove the lock file now.
rm -f "$ENCPASS_KEY_F/private.lock"
echo "Unlocked key $ENCPASS_KEY_NAME."
ENCPASS_NUM_KEYS_UNLOCKED=$(( ENCPASS_NUM_KEYS_UNLOCKED + 1 ))
else
echo "Error: The key file and/or lock file were not found as expected for key $ENCPASS_KEY_NAME."
fi
else
printf "Error: Failed to unlock key %s.\n" "$ENCPASS_KEY_NAME"
printf "       Please view %sfailed for details.\n" "$ENCPASS_KEY_F"
fi
else
echo "Error: No lock file found for the $ENCPASS_KEY_NAME key."
fi
fi
done
echo "Unlocked $ENCPASS_NUM_KEYS_UNLOCKED keys."
else
echo "No password entered."
fi
;;
dir )
shift
encpass_checks
echo "ENCPASS_HOME_DIR=$ENCPASS_HOME_DIR"
;;
help|--help|usage|--usage|\? )
encpass_checks
encpass_help
;;
* )
if [ ! -z "$1" ]; then
echo "Command not recognized. See \"encpass.sh help\" for a list commands."
exit 1
fi
;;
esac

虽然已经有很多答案,但还有一种方法可以从终端询问任何运行systemd的现代linux(是的,booo,我知道,但你能做些什么)。systemd-ask-password命令包含在标准的核心systemd包中,可以像这样使用:

#!/bin/sh
PASSWORD="$(systemd-ask-password "Enter your password:")"

使用--emoji=no开关抑制愚蠢的unicode锁字符。它在终端内工作得很好,但如果你需要它弹出一个GUI对话框,则需要调整,但这超出了这里的范围。