Shell 处理用户输入

2021-09-21 17:29:54
admin
2517
最后编辑:admin 于 2021-09-26 10:26:20

有时 Shell 脚本需要与使用者进行交互,Shell 提供了一些不同的方法来从用户处获得数据。包括:

  • 命令行参数(添加在命令后的数据,如有多个则用空格分隔开。也称为"位置参数")

  • 命令行选项(可修改命令行为的单个字母,如 -a  -r 等)

  • 直接从键盘读取输入

命令行参数

Linux 命令行中使用多个参数时,用空格分隔开。所以,如果参数中包含空格,就须要用引号引起来(单/双引均可)。

获取参数

shell 脚本通过特殊的变量(位置参数变量)来处理命令行参数(位置参数),包括所执行的脚本名称。(后文简称"位参")

位参变量是标准的数字:

$0 是当前执行的脚本名;

$1 是第一个参数;

$2 是第二个参数;

……

$9 是第九个参数。

当数字大于9时,位参变量名须用 {} 括起来,如:${10}  、${11}  ……

shell脚本会自动将命令行参数的值分配给位参变量,不需要你做任何处理。并可以在shell 脚本中像使用其他变量一样使用位参变量。

实例:

#!/bin/bash
name=$1
age=$2
echo 输出从命令行中自动获取的参数值:
echo 文件名为:$0
echo 姓名:$name
echo 年龄:$age

执行结果:

$ ./test.sh Tom 18
输出从命令行中自动获取的参数值:
文件名为:./test.sh
姓名:Tom
年龄:18

根据命令行的不同情况,$0可能包含了路径,如果在脚本中你只想要文件名,则可以是用 basename 命令过滤处理一下。

echo 文件名为: $(basename $0)

$ ./test.sh
文件名为: test.sh

验证参数

因为无法保证用户执行脚本时是否填写了参数,所以在脚本中使用位参变量时,要先进行验证是否有值。

if [ -n $1 ]         # -n 类似与其他编程语言里的 !empty()
then
……

特殊变量

Shell 内置提供了多个特殊变量 ,以供特殊应用场景下使用:

$# 用来包含脚本运行时,携带的命令行参数的个数。

$@ 变量会将命令行上提供的所有参数当作同一字符串中的多个独立的值,相当于是一个参数值列表。所以,可以使用for循环遍历该列表,从而获取每个参数值。

使用 getopts 处理参数选项

getopts 用于逐个(循环)处理命令行中的所有参数选项。

getopts命令格式:

getopts opts var

opts 指明了要查找的命令行选项字母。如果选项字母要求有个参数值,就在其后加一个冒号。如果要去掉错误信息,可以在opts前面加一个冒号。 

var 中保存当前迭代中的命令行参数选项。

每次调用它时,它一次只处理命令行上检测到的一个参数选项。然后在 opts 中查看是否有该参数选项,如果有则把选项存储到 var ,如果没有,则将 ?存储给 var。处理完所有参数后,它会退出并返回一个大于0的退出状态码。

注:getopts不能自动循环,它只是每运行一次,就依次处理命令行上检测到的一个参数选项。所以,它常和 while、case 循环配合使用。

getopts 命令会用到两个环境变量:

  • OPTARG:如果选项跟了一个参数值,则该值会自动保存到 OPTARG 中。

  • OPTIND:保存了参数列表中 getopts 正在处理的参数位置。

实例:

#!/bin/bash
while getopts :ab:c opt
do
  case "$opt" in
    a) echo "found the -a option";;
    b) echo "found the -b option,with value $OPTARG";;
    c) echo "found the -c option";;
    *) echo "Unknown option:$opt";;
  esac
done

$ ./test.sh -a -b gavin -c
found the -a option
found the -b option,with value gavin
found the -c option

getopts 会将命令行上找到的所有在 opts 中未定义的选项字母以 ?  的形式发送给代码。

直接获取用户输入

我们在 Linux 利用自动化脚本安装软件时经常遇到一些交互情景。比如想在脚本运行时问一个问题,让运行脚本的人回答(Y / N)。

这是如何实现的呢?

shell 提供了 read 命令,从标准输入(键盘)或另一个文件描述符中接受输入(文件描述符将在后文讲解)。在收到输入后,read 命令会将数据放进一个变量。

实例:

#!/bin/bash
echo -n "请输入你的姓名:"
read name
echo Hello,$name

执行结果:

./test.sh
请输入你的姓名:GavinHsueh
Hello,GavinHsueh

注:echo 使用 -n 选项,该选项不会在字符串末尾输出换行符,使得用户可以紧跟其后输入数据,而不是下一行,使操作更像表单。

或者,也可以直接使用 read 命令的 -p 选项,该选项可以直接指定提示符内容,下面代码和上面的代码效果是一样的:

#!/bin/bash
read -p "请输入你的姓名:" name
echo Hello,$name

注:如果要获取存储多个变量值,则只需在输入时将值用空格分隔,同时,对应指定的多个变量也用空格分隔。如果变量数量不够,则剩下的输入数据就全部分配给最后一个变量。

read 命令的其他常用选项:

  • read 命令的 -t 选项可以指定等待用户输入的秒数。当超时,read命令会返回一个非0退出状态码。

  • read 命令的 -s 选项可以隐藏用户在read命令中输入的数据,不在显示器上显示,例如密码。但会赋给变量,以便在脚本中使用。(此时输出的内容会与read提示符在同一行)

  • read 命令的 -n 选项要求用户输入特定数目的字符。例如 -n1 表明用户输入时只能输入一个字符。

从文件中读取数据

read 命令可以读取 Linux 文件里保存的数据。

每次调用 read 命令,它都会从文件中读取一行文本。当文件中再没有内容时,read命令会退出并返回非0退出状态码。

那首先要面对的一个问题是:如何将文件里的数据传给 read 命令?

最常见的方法是对文件使用 cat 命令,再将结果通过管道 | 直接传给含有 read 命令的 while 循环:

#!/bin/bash
count=1
cat ./test.sh | while read line
do
  echo "line $count:$line"
  count=$[ $count + 1 ]
done

执行结果:

$ ./test.sh    
line 1:#!/bin/bash
line 2:count=1
line 3:cat ./test.sh | while read line
line 4:do
line 5:echo "line $count:$line"
line 6:count=$[ $count + 1 ]
line 7:done