shell 利用read与键盘进行交互, 来取得变量的值
2008-11-06 11:26
使用read来进行变量分配
#!/bin/bash
# "Reading" 变量.
echo -n "Enter the value of variable 'var1': "
# -n 选项, 阻止换行.
read var1
# 注意: 在var1前面没有'$', 因为变量正在被设置.
echo "var1 = $var1"
echo
# 一个单独的'read'语句可以设置多个变量.
echo -n "Enter the values of variables 'var2' and 'var3' (separated by a space or tab): "
read var2 var3
echo "var2 = $var2 var3 = $var3"
# 如果你只输入了一个值, 那么其他的变量还是处于未设置状态(null).
exit 0
一个不带变量参数的read命令, 将会把来自键盘的输入存入到专用变量$REPLY中.
例子 11-4. 当使用一个不带变量参数的read命令时, 将会发生什么?
#!/bin/bash
# read-novar.sh
echo
# -------------------------- #
echo -n "Enter a value: "
read var
echo "\"var\" = "$var""
# 到这里为止, 都与期望的一样.
# -------------------------- #
echo
# ------------------------------------------------------------------- #
echo -n "Enter another value: "
read # 没有变量分配给'read'命令, 所以...
#+ 输入将分配给默认变量, $REPLY.
var="$REPLY"
echo "\"var\" = "$var""
# 这部分代码和上边的代码等价.
# ------------------------------------------------------------------- #
echo
exit 0
一般的, 当输入给read时, 输入一个\, 然后回车, 将会阻止产生一个新行. -r选项将会让 \ 转义.
例子 11-5. read命令的多行输入
#!/bin/bash
echo
echo "Enter a string terminated by a \\, then press <ENTER>."
echo "Then, enter a second string, and again press <ENTER>."
read var1 # 当 read $var1 时, "\" 将会阻止产生新行.
# first line 9 # second line
echo "var1 = $var1"
# var1 = first line second line
# 对于每个以 "\" 结尾的行,
#+ 你都会看到一个下一行的提示符, 让你继续向var1输入内容.
echo; echo
echo "Enter another string terminated by a \\ , then press <ENTER>."
read -r var2 # -r 选项会让 "\" 转义.
# first line 22
echo "var2 = $var2"
# var2 = first line 25
# 第一个 <ENTER> 就会结束var2变量的录入.
echo
exit 0
read命令有些有趣的选项, 这些选项允许打印出一个提示符, 然后在不输入ENTER的情况下, 可以读入你所按下的字符的内容.
# 不敲回车, 读取一个按键字符.
read -s -n1 -p "Hit a key " keypress
echo; echo "Keypress was "\"$keypress\""."
# -s 选项意味着不打印输入.
# -n N 选项意味着只接受N个字符的输入.
# -p 选项意味着在读取输入之前打印出后边的提示符.
# 使用这些选项是有技巧的, 因为你需要用正确的顺序来使用它们.
read命令的-n选项也可以检测方向键, 和一些控制按键.
例子 11-6. 检测方向键
#!/bin/bash
# arrow-detect.sh: 检测方向键, 和一些非打印字符的按键.
# 感谢, Sandro Magi, 告诉了我们怎么做到这点.
# --------------------------------------------
# 按键所产生的字符编码.
arrowup='\[A'
arrowdown='\[B'
arrowrt='\[C'
arrowleft='\[D'
insert='\[2'
delete='\[3'
# --------------------------------------------
SUCCESS=0
OTHER=65
echo -n "Press a key... "
# 如果不是上边列表所列出的按键, 可能还是需要按回车. (译者注: 因为一般按键是一个字符)
read -n3 key # 读取3个字符.
echo -n "$key" | grep "$arrowup" # 检查输入字符是否匹配.
if [ "$?" -eq $SUCCESS ]
then
echo "Up-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowdown"
if [ "$?" -eq $SUCCESS ]
then
echo "Down-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowrt"
if [ "$?" -eq $SUCCESS ]
then
echo "Right-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowleft"
if [ "$?" -eq $SUCCESS ]
then
echo "Left-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$insert"
if [ "$?" -eq $SUCCESS ]
then
echo "\"Insert\" key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$delete"
if [ "$?" -eq $SUCCESS ]
then
echo "\"Delete\" key pressed."
exit $SUCCESS
fi
echo " Some other key pressed."
exit $OTHER
# 练习:
# -----
# 1) 使用'case'结构来代替'if'结构,
#+ 这样可以简化这个脚本.
# 2) 添加 "Home", "End", "PgUp", 和 "PgDn" 这些按键的检查.
对于read命令来说, -n选项不会检测ENTER(新行)键.
read命令的-t选项允许时间输入(参考例子 9-4).
read命令也可以从重定向的文件中"读取"变量的值. 如果文件中的内容超过一行, 那么只有第一行被分配到这个变量中. 如果read命令的参数个数超过一个, 那么每个变量都会从文件中取得一个分配的字符串作为变量的值, 这些字符串都是以定义的空白字符来进行分隔的. 小心使用!
例子 11-7. 通过文件重定向来使用read命令
#!/bin/bash
read var1 <data-file
echo "var1 = $var1"
# var1将会把"data-file"的第一行的全部内容都为它的值.
read var2 var3 <data-file
echo "var2 = $var2 var3 = $var3"
# 注意, 这里的"read"命令将会产生一种不直观的行为.
# 1) 重新从文件的开头开始读入变量.
# 2) 每个变量都设置成了以空白分割的字符串.
# 而不是之前的以整行的内容作为变量的值.
# 3) 而最后一个变量将会取得第一行剩余的全部部分(译者注: 不管是否以空白分割).
# 4) 如果需要赋值的变量个数比文件中第一行以空白分割的字符串个数还多的话,
# 那么这些变量将会被赋空值.
echo "------------------------------------------------"
# 如何用循环来解决上边所提到的问题:
while read line
do
echo "$line"
done <data-file
# 感谢, Heiner Steven 指出了这点.
echo "------------------------------------------------"
# 使用$IFS(内部域分隔变量)来将每行的输入单独的放到"read"中,
# 前提是如果你不想使用默认空白的话.
echo "List of all users:"
OIFS=$IFS; IFS=: # /etc/passwd 使用 ":" 作为域分隔符.
while read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # I/O 重定向.
IFS=$OIFS # 恢复原始的$IFS.
# 这段代码也是Heiner Steven编写的.
# 在循环内部设置$IFS变量,
#+ 而不用把原始的$IFS
#+ 保存到临时变量中.
# 感谢, Dim Segebart, 指出了这点.
echo "------------------------------------------------"
echo "List of all users:"
while IFS=: read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # I/O 重定向.
echo
echo "\$IFS still $IFS"
exit 0
管道输出到read命令中, 使用管道echo输出来设置变量将会失败.
然而, 使用管道cat输出看起来能够正常运行.
cat file1 file2 |
while read line
do
echo $line
done
但是, 就像Bj鰊 Eriksson所指出的:
例子 11-8. 管道输出到read中的问题
#!/bin/sh
# readpipe.sh
# 这个例子是由Bjon Eriksson所编写的.
last="(null)"
cat $0 |
while read line
do
echo "{$line}"
last=$line
done
printf "\nAll done, last:$last\n"
exit 0 # 代码结束.
# 下边是脚本的(部分)输出.
# 'echo'出了多余的大括号.
#############################################
./readpipe.sh
{#!/bin/sh}
{last="(null)"}
{cat $0 |}
{while read line}
{do}
{echo "{$line}"}
{last=$line}
{done}
{printf "nAll done, last:$lastn"}
All done, last:(null)
变量(last)被设置在子shell中, 并没有被设置在外边.
在许多Linux发行版上, gendiff脚本通常都在/usr/bin下, 将find的输出通过管道传到while read结构中.
find $1 \( -name "*$2" -o -name ".*$2" \) -print |
while read f; do
. . .