shell编程(八) : [shell基础] 处理用户输入

接上一篇文章shell编程(七) : [shell基础] 使用结构化命令

3.3 处理用户输入

前面介绍了shell脚本的输出以及怎么处理这些输出,对于编程语言来说,有输出方法一定也会有输入方法。

3.3.1 命令行参数

命令行参数就是最基本的输入,命令也是脚本,命令通过判断不同的输入命令行参数来执行不同的功能。

1.位置参数

shell 脚本通过位置参数获取用户输入的命令行参数,位置参数变量是标准的数字:$0 是程序名,$1是第1个参数,$2是第2个参数,一次类推,这里注意,$0、$1、$2... 中的 0、1、2 不是数字而是字符,所以 $10会被解析为 $10,写一个简单的脚本验证一下:

在这里插入图片描述

给脚本传了两个参数 ab ,分别对应 $1$2 ,就像上面分析的一样,输出为 $10 的拼接,即 a0 。那要使用第10个参数怎么办呢,必须使用花括号,比如: ${10}

输入参数时,每个位置参数用空格隔开,如果需要在参数中包含空格,必须使用引号(单引号或双引号)将该参数括起来。

2.对参数进行测试

一个常见的规则,对于方法的入参一定要进行测试(检错),不然会发生意想不到的事情。常见的入参测试就是监测是否为空,前面有介绍到 test-n 参数,它可以监测入参是否为空。另外根据不同逻辑,还需要测试入参的其他属性,比如是否为数字/字符串等等。

3.3.2 特殊参数变量

bash shell 提供了一些特殊变量用于记录关于命令行参数的信息。

1.参数个数

特殊变量 $# 可以存储脚本运行时携带的命令行参数的个数,可以在脚本中任何地方使用这个特殊变量,就像普通变量一样,如下:

在这里插入图片描述

有没有发现,其实命令行参数是放在数组中的,元素0存放脚本文件名(命令名),其他元素存放参数,而在脚本中访问这个数组的元素的方法就是使用 $ -> $0、$1、$2等,可以想到最后一个元素就是 ${$#},但实际不是这样的,而是 ${!#},如下:

在这里插入图片描述

2.抓取所有的数据

有时脚本需要传入不定个数的参数,要获取这些参数,可以先用 $# 获取参数个数,再遍历所有参数。Linux还提供了两种更简单的方法 $*$@$* 变量会将命令行上提供的所有参数当作一个单词保存,$@ 变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词,也就是说$* 变量会将所有参数当成单个参数,而 $@ 变量会单独处理每个参数,如下:

在这里插入图片描述

3.3.3 移动变量

bash shell提供了另一种遍历命令行参数的方法,shift 命令,很适合在不知道参数个数的情况下使用。

shift 命令会根据它们的相对位置来移动命令行参数。默认情况下它会将每个参数变量向左移动一个位置,即变量 $3 的值会移到 $2 中,变量 $2 的值会移到 $1 中,而变量 $1 的值则会被删除(注意,变量 $0 的值,也就是程序名,不会改变),移动参数的同时,参数个数 $# 会减1,直到 $# 减到 1shift 不再移动参数位置,如下:

在这里插入图片描述

其中,while 测试$1 是否为空,不为空则继续循环, echo "Parameter #$count = $1" 先显示了 $1 ,之后使用 shift 将后面的参数向左移动,每次都显示$1 ,直到$1 为空,实现遍历所有命令行参数。

另外,shift 还可以控制移动距离,shift 后接移动的距离参数,如下:

在这里插入图片描述

可以看到,死循环了,这是因为 shift 会在参数个数 $# 小于移动距离的时候停止移动参数。

$1 = Tina 时,$# = 1 ,移动距离为2,所以 shift 不会再移动参数,而 while 的结束条件是 $1 为空,所以 while 永远不会结束。

3.3.4 处理选项

命令除了可以带参数,也可以带选项,选项是跟在单破折线后面的单个字母,它能改变命令的行为。

1. 处理简单的选项

同前面读取命令行参数的方法一样,也可以使用 shift 来读取命令行选项,如下:

在这里插入图片描述

case 语句会检查每个参数是不是有效选项。如果是的话,就运行对应 case 语句中的命令。
不管选项按什么顺序出现在命令行上,这种方法都适用。

有的选项也会带参数,这种方法也可以处理,如下:

在这里插入图片描述

例子中 -b 选项需要一个额外的参数,由于要处理的参数是 $1 ,额外的参数值就应该位于 $2,因为参数占了额外的一位,所以处理完要使用 shift 向后移动一位。如果选项有两个参数,这两个参数就位于 $2$3 ,最后就应该使用 shift 2 向后移动两位。

当然,对于带参数的选项,也可以作参数合法性的检测。

2. 分离参数和选项

日常使用中,经常遇到即带参数又带选项的的命令,这时使用上面的方法就行不通了,需要将参数和选项分开处理,Linux提供符号双破折线 -- 来实现这一功能。规定 -- 之前是命令的选项,之后是命令的参数,如下:

在这里插入图片描述

3. 更高级的选项处理方法

getopt

就像前面介绍到的, ls -l -h 命令可以简写成 ls -lh 一样,脚本命令选项也应该可以这么简写,但上面的介绍方法就不适用了,所以bash shell提供了 getopt 命令。

getopt 命令可以接受一系列任意形式的命令行选项和参数,并自动将它们转换成适当的格式。

格式如下:

getopt optstring parameters

optstring 定义了命令行有效的选项字母,还定义了哪些选项字母需要参数值。
首先,在 optstring 中列出你要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后加一个冒号。 getopt 命令会基于你定义的 optstring 解析提供的参数,如下:

getopt abc:d -a -bd -c parm parm1 parm2

-------------------output
-a -b -d -c parm -- parm1 parm2

getopt 自动将输入根据 optstring 部分的定义(即abc:d)解析成一般形式的选项和参数列表。同样,选项部分和参数部分用 -- 隔开,其中 parm 是选项 -c 的参数,parm1parm2 是命令的参数。

值得注意的是,如果输入的参数不符合 optstring 部分的定义,getopt 命令会产生一条错误消息。

getopt abc:d -a -bd -c parm -e parm1 parm2

-------------------output
getopt: invalid option -- 'e'
 -a -b -d -c parm -- parm1 parm2

虽然 getopt 生气地输出了错误信息,但它还是把正确的事情做完了,所以我们可以选择使用 getopt-q 选项来忽略输出的错误消息,如下:

getopt -q abc:d -a -bd -c parm -e parm1 parm2

-------------------output
 -a -b -d -c parm -- parm1 parm2

注意, 既然 -q 是属于 getopt 命令的,它就得紧跟在 getopt 的后面, optstring 的前面,不然会被后面的命令认错。

在脚本中使用 getopt 命令需要配合 set 命令,格式如下:

set -- $(getopt -q ab:cd "$@")

首先用 getopt 解析传入脚本的选项和参数,再将解析结果传给 set,其中 --set 的选项,它会将脚本的选项和参数替换成 set 命令的参数值。

set 命令可以将它的参数覆盖脚本的位置参数,如下:

在这里插入图片描述

虽然脚本的输入的参数为 1 2 3 ,但 set 命令将脚本参数替换成了 a b c

set -- $(getopt -q ab:cd "$@") 正式利用了这一点,位置参数不能解析类似 -ab 这样的组合选项,但脚本引用选项和参数又必须利用位置参数这一特性,所以先使用 getopt 解析好脚本的选项和参数,再传给 set 覆盖位置参数。其中有一个问题,就是当 set 接收到脚本的诸如 -a 这一的选项时,set 会认为这是set自己的选项,不会将其覆盖脚本的位置参数,而 -- 的作用就是无论set接收到什么都将其覆盖到脚本的位置参数。如下:

在这里插入图片描述

set 后面没有使用 -- 选项,结果当遇到 set 没有的选项 -c 时就报错了。可以看到 set也没有-d 参数,但没有报 -d 的错误,这是因为Linux命令行错误会只报遇到的第一个错误,可以自己尝试一下,把脚本的-c 去掉,就会报-d 的错误了。

在这里插入图片描述

这次使用了set--选项,结果没有报错可以正常输出。

但仍然有一个问题,就是getopt 不能解析带空格的参数,只要遇到空格,getopt就会认为是两个参数,如下:

在这里插入图片描述

本想给-b选项输入值为 hello vistar 的参数,为了让它看起来是一个整体,还想前面介绍echo一样加了"" ,结果于事无补,getopthello vistar 解析成了两个参数,然而getopt没有解决向这一问题的办法,所以又有了getopts命令,有说后面的s是复数,但我觉得是super,因为getoptsgetopt厉害了。

getopts

getopt 将命令行的选项和参数处理后只生成一个输出,而 getopts 命令每次调用时,只处理命令行上检测到的一个参数。处理完所有的参数后,它会退出并返回一个大于0的退出状态码。

getopts 命令的格式如下:

getopts optstring variable

optstringgetopt 命令的类似,有效的选项字母都会列在 optstring 中,如果选项字母要求有一个参数值,就加一个冒号。

不同的是,要去掉错误消息的话,可以在 optstring 之前加一个冒号。

getopts 命令将当前参数保存在命令行中定义的 variable 中,这个变量名可以自定义,每次调用 getopts ,通过这个变量访问命令行的参数。

getopts 命令会用到两个环境变量。每次调用 getopts 时,如果选项需要跟一个参数, OPTARG 环境变量就会保存这个值,如果选项后面没有参数,该环境变量存储的值为空。

示例如下:

在这里插入图片描述

OPTIND 环境变量保存了参数列表中 getopts 正在处理的参数位置,初始值为1。getopts 命令在处理每个选项时,会将 OPTIND 环境变量值增一,在 getopts 完成选项的处理后,可以使用 shift 命令和 OPTIND 值来访问脚本的参数,如下:

在这里插入图片描述

其中,var-b 的参数,var1var2 是脚本的参数。

另外,getopts 还有几个的特点,

  1. 它解析之后的选项不带单破折线-,从上面case 语句中的分支中可以看到;
  2. 在命令行里输入选项和参数时,可以不用空格隔开,如下;
  3. 可以把命令行上找到的未定义的选项输出成问号,如下:

在这里插入图片描述

3.3.5 业内统一的选项含义

在 bash shell 脚本圈子里,有些字母已经有约定俗成的含义,我们在写bash脚本时,遵循这一约定,不仅使自己脚本看起来高级,也有利于别人使用。

一些字母的常用含义如下:

选项描述
-a显示所有对象
-c计数
-d指定一个目录
-e扩张一个对象
-f指定读入数据的文件
-h显示命令的帮助信息
-i忽略文本大小写
-l产生输出的长格式文本
-n使用非交互模式(批处理)
-o将所有输出重定向到指定的输出文件
-q以安静模式运行
-r递归地处理目录和文件
-s以安静模式运行
-v生成详细输出
-x排除某个对象
-y对所有问题回答yes
3.3.6 获取用户输入-read

获取选项和参数只是一种简单的获取用户输入的方法,有时在脚本运行的过程中,为了提高和用户的交互性能,也需要获取用户的输入,bash shell为此提供了 read 命令。

1.read的基本使用

read 命令从标准输入(键盘)或另一个文件描述符中接收输入。在收到输入后, read 命令会将数据放进一个变量,有点像input函数,如下:

在这里插入图片描述

也可以使用 -p 选项(prompt)将输入提示直接放在 read 后面,如下:

在这里插入图片描述

read会将输入的数据都存入变量,但如果需要输入多个数据,并单独处理,可以定义多个变量,read 会按空格分割,将数据存入每个变量中,如果输入的数据比定义的变量个数多,会将多余的数据都存入最后一个变量,如下:

在这里插入图片描述

另外,read 也可以不指定变量,这时它会把数据存入环境变量REPLY中,使用方法和其他变量一样。

2.超时管理

read 等待用户输入时,如果一直等不到输入,会一直等下去,但有时希望如果没有输入要继续往下执行,这是可以使用-t 选项设置超时时间,等待超过这一时间后,read 命令会返回一个非零退出状态码,如下,设置5s超时:

在这里插入图片描述

3.不显示读取内容

我们在Linux上输入密码时,是不会显示密码的,这个功能可以使用read-s 选项实现,如下:

在这里插入图片描述

4.从文件中读取内容

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

通常使用cat 命令获取文件的全部内容,然后用管道 | 传给 read 命令,如下:

在这里插入图片描述

其中,使用count记录string文件的行号,每循环一次read会读取一行内容。

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万俟淋曦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值