Linux中read命令简介
1. 简介
当我们创建 Bash 脚本时,接受用户输入会很方便。在本教程中,我们将了解如何使用*read *命令执行此操作。
Bash read命令是一个强大的内置实用程序,用于在 Linux 下对字符串进行分词 。
由于它是一个内置命令,只要我们有 Bash 可用,就不需要额外的设置步骤。
2.基本语法
让我们探索包括所有可选参数的基本语法:
$ read [options] [name...]
选项参数影响read命令与输入交互的方式。我们将在接下来的部分中探讨这些。
name参数指定将拆分操作产生的实际单词存储在哪些变量中。
2.1. 默认行为
** read的无参数调用从标准输入流中获取一行并将其分配给REPLY内置变量**:
$ read
blogdemo is a cool tech site # what we type
$ echo $REPLY
blogdemo is a cool tech site
现在让我们指定一些变量来存储结果:
$ read input1 input2 input3
blogdemo is a cool tech site # what we type
$ echo "[$input1] [$input2] [$input3]"
[blogdemo] [is] [a cool tech site]
如果变量的数量少于获得的单词,则所有剩余的单词及其分隔符都被塞入最后一个变量中。
默认情况下,read命令将输入拆分为单词,将space、tab和换行字符视为单词分隔符。
我们可以使用特殊的 \ 字符在多行上传递输入:
$ read input1 input2 input3
blogdemo \ # what
is a cool \ # we
tech site # type
$ echo "[$input1] [$input2] [$input3]"
[blogdemo] [is] [a cool tech site]
让我们仔细看看这个例子。除了第 4 行,我们使用 \ 来转义每一行(2 和 3)上的换行字符。
换行字符还用作默认输入行分隔符。我们稍后会看到如何改变这种行为。
2.2. 内部字段分隔符
内部字段分隔符(IFS)确定给定行中的字边界。我们可以修改它以适应我们的需要并处理自定义行:
$ {
IFS=";"
read input1 input2 input3
echo "[$input1] [$input2] [$input3]"
}
blogdemo;;is;a;cool;tech;site # what we type
[blogdemo] [] [is;a;cool;tech;site]
请注意,第二个单词是空的,因为两个分号字符之间没有任何内容。
让我们再次运行相同的函数,但现在让我们使用空格作为内部字段分隔符:
$ {
IFS=" "
read input1 input2 input3
echo "[$input1] [$input2] [$input3]"
}
blogdemo is a cool tech site # what we type
[blogdemo] [is] [a cool tech site]
这次输出发生了变化,因为分词的行为与空格字符不同。空格字符序列被视为分隔符。
2.3. 返回代码
成功时返回码为 0。如果发生错误或遇到EOF ,则返回码大于 0:
$ {
read input1-abc
echo "return code [$?]"
}
bash: read: `input1-abc': not a valid identifier
return code [1]
在这个小片段中,我们尝试传递一个无效的变量名,然后我们打印退出状态。
2.4. 添加基本选项
让我们看一下我们可以使用的一些最基本的选项:
- -a 数组:将单词拆分操作的结果存储在数组中而不是单独的变量中
- -e:使用 Bash 内置的Readline 库读取输入行
- *-s:*不将输入行回显到标准输出流
- -p prompt : 在从标准输入流中请求输入之前打印提示文本,不带换行字符
- -i text:在标准输出流上打印文本作为默认输入(只能与*-e*结合使用)
现在让我们使用*-s和-i*选项实现一个带有隐藏字符的简单密码提示:
$ {
prompt="You shall not pass:"
read -p "$prompt" -s input
echo -e "\n input word [$input]"
}
You shall not pass: # invisible input here
input word [baledung is a cool site]
在某些情况下,单词的数量因输入而异,因此我们可以利用基于数组的变量:
$ {
declare -a input_array
text="blogdemo is a cool tech site"
read -e -i "$text" -a input_array
for input in ${input_array[@]}
do
echo -n "[$input] "
done
}
blogdemo is a cool tech site # default input here
[blogdemo] [is] [a] [cool] [tech] [site]
-i 选项允许我们 **指定默认输入行。借助Readline内置库,**我们还可以使用 -e 选项轻松编辑输入。
3. 高级语法
现在我们已经看到了read的实际应用,让我们来看看一些更高级的选项:
- -d delim:指定输入行的分隔符而不是使用换行字符
- -u *fd:*从给定的文件描述符中读取输入行
- *-r:按原样处理 \ *字符(不能用于转义特殊字符)
- -t timeout:尝试在给定的秒数内读取输入
- -N:从输入中准确读取 N 个字符,除非发生超时或达到EOF
3.1. 从文件中读取
当我们想要处理文件中的特定字段时,从文件中读取是非常有用的。
让我们看一下汽车的一些简单的结构化数据:
car,car model,car year,car vin
Mercury,Grand Marquis,2000,2G61S5S33F9986032
Mitsubishi,Truck,1995,SCFFDABE1CG137362
现在让我们考虑从这个CSV文件中提取特定字段并打印出特定字段:
$ {
exec {file_descriptor}<"./file.csv"
declare -a input_array
while IFS="," read -a input_array -u $file_descriptor
do
echo "${input_array[0]},${input_array[2]}"
done
exec {file_descriptor}>&-
}
在这个例子中,我们首先调用exec 内置 bash 命令来打开我们输入的文件描述符。然后,我们在while 循环中将其传递给read命令,允许我们逐行读取文件。
最后,我们通过再次调用exec命令来关闭文件描述符。
请注意,**我们将IFS定义为while 循环的一部分。**这确保了循环外的进一步分词操作将使用默认的IFS 。
这给了我们以下输出:
car,car year
Mercury,2000
Mitsubishi,1995
我们在开头提到我们可以更改默认输入行分隔符。
让我们考虑使用分号分隔的行而不是换行字符来构造输入的情况:
car,car model,car year,car vin; \
> Mercury,Grand Marquis,2000,2G61S5S33F9986032; \
> Mitsubishi,Truck,1995,SCFFDABE1CG137362;
我们现在修改我们的函数以考虑新的行分隔符:
$ {
# same calls as before
while IFS="," read -a input_array -d ";" -u $file_descriptor
# same calls as before
}
这提供了与以前相同的输出。
3.2. 从其他命令中读取
我们还可以通过**管道重定向将 read命令与其他 Linux 命令结合使用**。
让我们重定向ls 命令的输出并从* ⁄根)*文件夹中提取文件名及其访问权限 :
$ {
ls -ll / | { declare -a input
read
while read -a input;
do
echo "${input[0]} ${input[8]}"
done }
}
每个 Linux 发行版的输出都不同,但是,我们可以看到截断的输出,正如我们所期望的那样:
drwxr-xr-x bin
drwxr-xr-x boot
drwxr-xr-x dev
# some more folders
让我们深入了解一下这个例子。首先,我们执行*ls -ll /*命令打印根目录下的文件。
然后,我们将输出通过管道传输到调用read的命令组 的标准输入。
这样,我们就可以迭代处理多行输入,并打印每行的第一列和第八列。
我们还在循环之前执行一次read以跳过ls在顶部打印的摘要。当我们想要避免表头时,这非常有用。
3.3. 超时和特殊字符
在复杂的脚本中,我们可能需要更大的灵活性来避免阻塞read调用。 此外,输入可能包含我们不想转义的特定 \ 字符(例如在生成的密码中):
{
prompt="You shall not pass:"
read -p "$prompt" -s -r -t 5 input
if [ -z "$input" ]; then
echo -e "\ntimeout occured!"
else
echo -e "\ninput word [$input]"
fi
}
请注意,我们使用了*-t选项并指定了 5 秒的超时时间。这确保了如果在此时间间隔内没有输入任何输入(包括默认的换行*符),read命令将自动返回。
由于我们说过反斜杠应该按字面意思理解,因此不再可能像在前面的示例中那样跨多行输入输入:
You shall not pass: # invisible input here
input word [blogdemo\is]
3.4. 准确读取 N 个字符
让我们把事情进一步复杂化,假设我们希望输入中正好有 11 个字符:
{
prompt="Reading exactly 11 chars:"
read -p "$prompt" -N 11 -t 5 input1 input2
echo -e "\ninput word1 [$input1]"
echo "input word2 [$input2]"
}
当我们运行上面的例子时,输出有点令人惊讶:
Reading exactly 11 chars:blogdemo is # no <newline> here
input word1 [blogdemo is]
input word2 []
引入 -N 选项会导致三个主要副作用。
首先,行分隔符不再重要。read命令将等待输入的确切字符数退出(因此在这里设置超时也是有意义的)。
其次,它不再将输入拆分为单词,因为我们希望将 11 个字符分配给input1。
最后,如果发生超时, read甚至会将部分输入分配给input1变量。