Shell脚本read用法实现

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

Shell脚本read用法实现

linux运维那些事儿   2023-02-03 我要评论

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可以防止继续执行不必要的代码。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们