Shell模拟密码输入

之前在网上看到一个帖子,说是要使用shell模拟密码输入功能。平常使用sudo命令时,就会有输入密码提示,shell会屏蔽掉所有的键盘输入(不显示“*”,什么都不显示★_★)。之后查阅各种资料,找到了两种比较简单的实现方法。


1、read -s

read读取用户的输入并将其存入指定的变量中。指定-s选项后,输入将不回显。于是实现代码如下:

#!/bin/bash
 
echo -n "Username: "
read username
 
echo -n "Password: "
read -s password
 
echo -e "\n"
 
echo "Username is $username"
echo "PassWord is $password"

简要分析,echo的-n选项指定不默认输出回车换行符(\n,echo在默认情况下输出信息后会自动回车换行),而-e选项让echo可以识别输出信息中的转义字符。read分别以回显与不回显方式存储用户输入的username和password。

那么输出结果如下:

Username: Linus

Password:


UserName is Linus

PassWord is I don't know


2、stty -echo

stty用于打印或更改终端的特性。在这里用到了-echo选项,查看man手册,它用于屏蔽输入字符(即输入不回显。但这里要注意,-echo选项仅仅是屏蔽了输入字符的回显,对其他输出没有限制。例如在shell脚本中有如下两条命令:

stty -echo

echo "Can you see me?"

第二行依旧会输出到终端上,因为它不是由输入设备产生的)。于是实现代码如下:

#!/bin/bash
 
STTY_RESTORE=$(stty -g)
 
echo -n "Username: "
read username
 
stty -echo
echo -n "Password: "
read password
 
echo -e "\n"
echo "Username is $username"
echo "Password is $password"
 
stty $STTY_RESTORE

简要分析,stty的-g选项会输出当前终端特性的参数,在程序起始处将终端特性保存,为了在程序执行完毕后恢复原先的终端特性(程序的最后一行)。在输入密码前使用stty -echo命令屏蔽输入回显。

那么输出结果如下:

Username: Linus

Password:


Username is Linus

Password is Oh, I told you I don't know!


需要注意的是,read是bash shell的内嵌命令,而stty不是。那么也就是说,如果换为另一种shell,有可能没有read命令,也有可能没有read的-s选项。再换句话说,用stty实现的程序可移植,而用read实现的程序则不一定。


至此,实现了shell模拟密码输入的基本功能。然而我又想到,平常见到最多的还是输入密码时会回显一个“*”或是其他之类的字符,同时觉得这样也比较人性化。于是继续查阅各种资料,找到了一种实现方法http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=543875,首先要感谢lijunling所提供的实现思路。不过该代码在我的机器上(对了,忘了自我介绍,我使用的是Ubuntu 11.04,shell为GNU bash shell 4.2.8(1)-release (i686-pc-linux-gnu))运行异常,无法识别回车,并且在快击键盘时,有的字符会以明文显示且未记录为密码。于是按照该代码的大体思路继续查阅资料,最终在我的机器上实现了同样的功能。此外我还做了一些优化,解决了快击键盘的问题以及增加了退格键的功能。

3、dd if=/dev/tty bs=1 count=1 2> /dev/null; stty -echo cbreak

dd用于将输入内容进行随意的筛选、转换等相应操作后拷贝到输出(默认是标准输入到标准输出),这里用它来获得输入的字符。于是实现代码如下:

#!/bin/bash
 
STTY_RESTORE=$(stty -g)
 
echo -n "Username: "
read username
 
echo -n "Password: "
stty -echo cbreak
while true
do
        character=$(dd if=/dev/tty bs=1 count=1 2> /dev/null)
        case $character in
        $(echo -e "\n"))
                break
                ;;
        $(echo -e "\b"))
                if [ -n "$password" ]; then
                        echo -n -e "\b \b"
                        password=$(echo "$password" | sed 's/.$//g')
                fi
                ;;
        *)
                password=$password$character
                echo -n '*'
                ;;
        esac
done
 
stty $STTY_RESTORE                      #stty -cbreak echo

echo -e "\n\nUsername is $username"
echo "Password is $password"

简要分析,这次使用stty还多指定了cbreak选项。查看man手册,它是erase、kill、wearse和rprnt选项的集合。它所实现的功能是,当输入一个字符后立即上报,而不是等到输入回车之后一起上报。接着进入一个死循环,该循环中会保存输入的字符,然后进入分支,根据输入的字符执行相应操作。

dd命令指明输入文件为/dev/tty,也就是标准输入,每次读写1个字节(bs),仅拷贝输入的1块内容(count),未指明输出文件,也就是输出到标准输出。由于dd命令在执行过程中会输出一些提示信息,而实际不想输出这些信息,所以需要将其重定向到/dev/null设备。那么经过这条语句,character也就得到了输入的字符。

接下来进入分支,如果是回车键,那么跳出死循环,结束密码输入。如果是退格键,那么判断存储的密码(password)如果非空,则让光标前移一格,输出空格,之后再前移一格(“\b \b”),这样就删除了一个星号显示。同时,将存储的密码的最后一个字符删除(sed 's/.$//g',匹配最后一个字符并替换为空字符)。最后,如果输入的是其他的字符,则将其追加到password中,并输出一个星号。

密码输入完成后,使用stty恢复之前的终端特性。也可以使用注释中的命令,明确指定恢复之前的何种特性。

那么输出结果如下:

Username: Linus

Password: **********************


Username is Linus

Password is Oh, my god! Help me!!!

 

在实际测试过程中遇到的问题:

1、程序第18行,如果$password不加双引号,则无法判断出该变量是否为空字符串。

2、程序第20行,如果写成echo $password(未加双引号),则echo会自动过滤掉变量中行首与行尾的空格。举个例子,输入[空格]a[空格]b[空格][退格],则echo管道给sed的值为a[空格]b。而此时有4个星号(输入了5-1个字符),但password中只存储了2个字符(a[空格],b被删除了)。

3、该程序不能识别F1~F12、ESC等功能键,因为这些功能键由多个字符表示,但stty cbreak使得每个字符都会立即上报,从而会显示多个星号。

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值