1.概述
到目前为止,编写的脚本都缺少一个常用于大多数计算机程序的特性–交互性,或者说与用户互动的能力。尽管很多程序并不需要是交互式的,但能够直接接受用户输入,确实有利于某些程序。
2. read从标准输入读取值
内建命令read可用于从标准输入中读取一行。该命令可以读取键盘输入,如果使用了重定向,也可以读取文件的数据行。该命令用法如下:
read [-options] [variable...]
其中,option是一个或多个选项,variable是一个或多个变量,用于保存输入值。如果未指定变量,则输入值保存在Shell变量REPLAY中。使用read命令改写之前的整数验证脚本:
#!/bin/bash # read-integer echo -n "Please enter an integer -> " read int if [[ "$int" =~ ^-?[0-9]+$ ]]; then if [ "$int" -eq 0 ]; then echo "$int is zero." else if [ "$int" -lt 0 ]; then echo "$int is negative." else echo "$int is positive" fi fi if [ $((int % 2)) -eq 0 ]; then echo "$int is even." else echo "$int is odd." fi else echo "Input value is not an integer." >&2 exit 1 fi
read也可以将输入赋给多个变量
[sysadmin@ansible bin]$ cat read1 #!/bin/bash #read-multiple echo -n "Enter one or more values > " read var1 var2 var3 var4 var5 echo "var1 = '$var1'" echo "var2 = '$var2'" echo "var3 = '$var3'" echo "var4 = '$var4'" echo "var5 = '$var5'" [sysadmin@ansible bin]$ read1 Enter one or more values > 1 2 3 4 5 var1 = '1' var2 = '2' var3 = '3' var4 = '4' var5 = '5'
如果read接收到的值数量少于预期,则多出的变量为空值,如果数量多于预期,则额外的输入全部保存在最后一个变量中。
[sysadmin@ansible bin]$ cat read1 #!/bin/bash #read-multiple echo -n "Enter one or more values > " read var1 var2 var3 var4 var5 echo "var1 = '$var1'" echo "var2 = '$var2'" echo "var3 = '$var3'" echo "var4 = '$var4'" echo "var5 = '$var5'" [sysadmin@ansible bin]$ read1 Enter one or more values > a var1 = 'a' var2 = '' var3 = '' var4 = '' var5 = '' [sysadmin@ansible bin]$ read1 Enter one or more values > a b c d e f g var1 = 'a' var2 = 'b' var3 = 'c' var4 = 'd' var5 = 'e f g' [sysadmin@ansible bin]$
如果没有为read命令指定变量,则所有的输入全部保存在Shell变量REPLY中
[sysadmin@ansible bin]$ cat read2 #!/bin/bash #read-multiple echo -n "Enter one or more values > " read echo "REPLY = '$REPLY'" [sysadmin@ansible bin]$ read2 Enter one or more values > a b c d REPLY = 'a b c d' [sysadmin@ansible bin]$
2.1 read选项
选项 | 描述 |
---|---|
-a array | 将输入分配给数组(从索引0开始)。 |
-d delimiter | 将字符串delimter中的第一个字符(而非换行符)作为输入的结束 |
-e | 使用readline处理输入,允许使用和命令行相同的方式编辑输入 |
-i string | 如果用户直接按Enter键,使用string作为默认值,需要配合-e选项使用 |
-n num | 从输入中读取num个字符,而非读取一整行 |
-p prompt | 将字符prompt作为输入提示来显示 |
-r | 原始模式,不将反斜线符解释为转义 |
-s | 静默模式,在用户输入字符时不回显,该模式适用于输入密码或其他机密信息 |
-t seconds | 超时,seconds秒之后终止输入,如果输入超时,read返回非0退出状态值 |
-u fd | 从文件描述符fd中读取输入 |
选项-p
[sysadmin@ansible bin]$ cat read2 #!/bin/bash #read-single read -p "Enter one or more values > " echo "REPLY = '$REPLY'" [sysadmin@ansible bin]$ read2 Enter one or more values > a REPLY = 'a' [sysadmin@ansible bin]$
选项-t和-s
[sysadmin@ansible bin]$ cat readpass #!/bin/bash #read-pass if read -t 10 -sp "Enter password > " passwd; then echo -e "\nSecret passphrase = '$passwd'" else echo -e "\nInput timed out" >&2 exit 1 fi [sysadmin@ansible bin]$ readpass Enter password > Secret passphrase = 'abcdef' [sysadmin@ansible bin]$
选项-e和-i
[sysadmin@ansible bin]$ cat readdefault #!/bin/bash #read-default read -e -p "What is your user name? " -i $USER echo "You answered: '$REPLY'" [sysadmin@ansible bin]$ readdefault What is your user name? sysadmin You answered: 'sysadmin' [sysadmin@ansible bin]$
2.2 IFS
Shell通常会提供给read的输入进行单词分割,这意味着输入行中被一个或多个空白字符分割的多个单词会变成若干独立项,再由read分配给各个变量。Shell变量内部字段分割符(Internal Filed Separator,IFS)控制着此行为。IFS的默认值包含了空格符,制表符,换行符,它们都可用于分割单词。
我们可以调整IFS的值,控制read的输入字段。例如:/etc/passwd文件中的数据行采用冒号作为字段分隔符。将IFS的值改成冒号,就可以使用read读入/etc/passwd的内容并顺利将字段分割存入各个变量。来看下面的实现脚本。
[sysadmin@ansible bin]$ cat read-ifs #!/bin/bash # read-ifs FILE=/etc/passwd read -p "Enter a user > " user_name file_info="$(grep "^$user_name:" $FILE)" if [ -n "$file_info" ]; then IFS=":" read user pw uid gid name home shell <<< "$file_info" echo "User = '$user'" echo "UID = '$uid'" echo "GID = '$gid'" echo "Full Name = '$name'" echo "Home Dir = '$home'" echo "Shell = '$shell'" else echo "No such user '$user_name'" >&2 exit 1 fi [sysadmin@ansible bin]$ read-ifs Enter a user > jticnoc User = 'jticnoc' UID = '2099' GID = '2099' Full Name = '' Home Dir = '/home/jticnoc' Shell = '/bin/bash' [sysadmin@ansible bin]$
Shell允许一个或多个变量赋值直接出现在命令之前。这些赋值会修改紧随其后的命令的环境。这种赋值效果是临时的。对环境所作的改动仅限于命令执行期间有效。<<< 标识here string。here string类似于here document,但是更简短,仅由单个字符串组成。
2.3 验证输入
好程序和差程序之间的区别往往在于处理意外情况的能力,而意外情况多以错误输入的形式出现。重要的是,每次程序接收到输入的时候,都要执行此类检查,以防止非法数据。
[sysadmin@ansible bin]$ cat read-validate #!/bin/bash #read-validate invalid_input () { echo "Invalid input '$REPLAY'" >&2 exit 1 } read -p "Enter a single item > " # input is null [[ -z "$REPLY" ]] && invalid_input # input is multi items (( "$(echo "$REPLY" |wc -w)" > 1 )) && invalid_input # input filename is valid if [[ "$REPLY" =~ ^[-[:alnum:]\._]+$ ]]; then echo "'$REPLY' is a valid filename." if [[ -e "$REPLY" ]]; then echo "And file '$REPLY' exists." else echo "However, file '$REPLY' does not exist." fi # input is float if [[ "$REPLY" =~ ^-?[[:digit:]]*\.[[:digit:]]+$ ]]; then echo "'$REPLY' is a floating point number." else echo "'$REPLY' is not a floating point number." fi # input is int if [[ "$REPLY" =~ ^-?[[:digit:]]+$ ]]; then echo "'$REPLY' is an integer." else echo "'$REPLY' is not an integer." fi else echo "The string '$REPLY' is not a valid filename." fi [sysadmin@ansible bin]$
2.4 菜单
菜单驱动是一中常见的交互方式,菜单驱动的程序会为用户呈现一系列的选项,要求用户从中选择。
[sysadmin@ansible bin]$ cat read-menu #!/bin/bash #read-menu clear echo " Please Select: 1.Display System Information 2.Display Disk Space 3.Display Home Space Utilization 4.Quit " read -p "Enter selection [0-3] > " if [[ "$REPLY" =~ ^[0-3]$ ]]; then if [[ "$REPLY" == 0 ]]; then echo "Program terminated." exit fi if [[ "$REPLY" == 1 ]]; then echo "Hostname: $HOSTNAME" uptime exit fi if [[ "$REPLY" == 2 ]]; then df -h exit fi if [[ "$REPLY" == 3 ]]; then if [[ "$(id -u)" -eq 0 ]]; then echo "Home Space Utilization (ALL Users)" du -sh /home/* else echo "Home Space Utilizaion ($USER)" du -sh "$HOME" fi exit fi else echo "Invalid entry." >&2 exit 1 fi
第一部分展示了菜单并获取用户输入,第二部分识别输入并执行相应的菜单项功能。注意脚本中exit命令的用法,在完成用户选定的功能后,exit可以防止继续执行不必要的代码。
到此这篇关于Shell脚本read用法实现的文章就介绍到这了,更多相关Shell read内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!