Bash 用户交互之 read

前言

我们说过Linux的哲学思想之一就是“尽量避免跟用户交互”(详见《magedu pro 第一周作业》),但这种哲学思想更多的是用在命令的执行上,对于我们的脚本及程序设计来说,有些交互还是很有必要的。

之前介绍过使用 whiptail 来进行对话框式的交互(详见《如何在交互式 shell 脚本中创建对话框
),确实使用 whiptail 能够更直观地让用户进行交互,功能也比较强大,但是如果只是实现一些简单的交互,使用 whiptail 就不太必要了,这时候我们可以使用轻量级的交互方式,那就是 read

简介

read 是从标准输入捕获内容,然后赋值给指定变量的命令,执行格式为:

read [option]... [name ...]

一个简单的示例:

1
2
3
4
5
6
7
[hzz@magedu ~]$ read ABC
abc123
[hzz@magedu ~]$ echo $ABC
abc123
[hzz@magedu ~]$

# 将终端输入的 Just ABC! 赋值给了 ABC 变量。

值得注意的是,若提供的参数少于变量数,则多出的变量为空;若提供的参数多余变量数,则最后一个变量赋于剩余的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[hzz@magedu ~]$ read ABC DEF
123
[hzz@magedu ~]$ echo $ABC
123
[hzz@magedu ~]$ echo $DEF # 此时由于赋值不够,DEF 变量为空。

[hzz@magedu ~]$
[hzz@magedu ~]$
[hzz@magedu ~]$ read QWE ASD
123 456 789
[hzz@magedu ~]$ echo $QWE
123
[hzz@magedu ~]$ echo $ASD # 此时由于赋值过多,ASD 变量被赋予剩余的值。
456 789
[hzz@magedu ~]$

参数

centos7 中 read 的文档直接被翻译成了中文,省事了许多,哈哈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
[hzz@magedu ~]$ help read
read: read [-ers] [-a 数组] [-d 分隔符] [-i 缓冲区文字] [-n 读取字符数] [-N 读取字符数] [-p 提示符] [-t 超时] [-u 文件描述符] [名称 ...]
从标准输入读取一行并将其分为不同的域。

从标准输入读取单独的一行,或者如果使用了 -u 选项,从文件描
述 FD 中读取。该行被分割成域,如同词语分割一样,并且
第一个词被赋值给第一个 NAME 变量,第二个词被赋值给第二个
NAME 变量,如此继续,直到剩下所有的词被赋值给最后一个
NAME 变量。只有 $IFS 变量中的字符被认作是词语分隔符。

如果没有提供 NAME 变量,则读取的行被存放在 REPLY 变量中。

选项:
-a array 将词语赋值给 ARRAY 数组变量的序列下标成员,从
零开始。
-d delim 持续读取直到读入 DELIM 变量中的第一个字符,而不是
换行符
-e 在一个交互式 shell 中使用 readline 获取行
-i text 使用 TEXT 文本作为 readline 的初始文字
-n nchars 读取 nchars 个字符之后返回,而不是等到读取
换行符,但是分隔符仍然有效,如果遇到分隔符之前读
取了不足 nchars 个字符
-N nchars 在准确读取了 nchars 个字符之后返回,除非遇到
了文件结束符或者读超时,任何的分隔符都被忽略
-p prompt 在尝试读取之前输出 PROMPT 提示符并且不带
换行符
-r 不允许反斜杠转义任何字符
-s 不显示终端的任何输入
-t timeout 如果在 TIMEOUT 秒内没有读取一个完整的行则
超时并且返回失败。TMOUT 变量的值是默认的超时时间。
TIMEOUT 可以是小数。如果 TIMEOUT 是0,那么仅当在
指定的文件描述符上输入有效的时候,read 才返回成功。
如果超过了超时时间,则返回状态码大于128
-u fd 从文件描述符 FD 中读取,而不是标准输入

退出状态:
返回码为零,除非遇到了文件结束符,读超时,或者无效的文
件描述符作为参数传递给了 -u 选项。
readarray: readarray [-n 计数] [-O 起始序号] [-s 计数] [-t] [-u fd] [-C 回调] [-c 量子] [数组]
从一个文件中读取行到数组变量中

一个 `mapfile'的同义词。
readonly: readonly [-aAf] [name[=value] ...] or readonly -p
标记 shell 变量为不可改变。

标记每一个 NAME 名称为只读;这些 NAME 变量的值将不可以被后续的赋值
操作所改变。如果提供了 VALUE,则在标记为只读之前将 VALUE 值赋给变量。

选项:
-a 指下标数组变量
-A 指关联数组标量
-f 指 shell 函数
-p 显示只读变量和函数列表

`--' 的参数禁用进一步的选项处理。

退出状态:
返回成功,除非使用了无效的选项或者 NAME 名称。
[hzz@magedu ~]$

应用

read 命令其实在命令行里的应用是有限的,主要还是在脚本里面的应用。

下面是一个可以由用户自主输入用户名和密码的以创建用户的脚本示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash

read -p "Enter a username: " name
[ -z "$name" ] && echo "a username is needed." && exit 2

# 为了保护隐私,此处输入密码无任何显示。
read -p -s "Enter password for $name, [password]: " password
[ -z "$password" ] && password="password"

if id $name &> /dev/null; then
echo "$name exists."
else
useradd $name
echo "$password" | passwd --stdin $name &> /dev/null
echo "Add user $name finished."
fi