处理用户输入:
1、命令行参数
向 shell 脚本传递数据的最基本的方法是使用命令行参数,命令行参数允许在运行脚本时向命令行添加数据。
2、读取参数
bash shell 中有一些特殊变量叫做 位置参数,位置变量参数是标准的数字:$0 是程序名,$1 是第一个参数,$2 是第二个参数,以此类推,直到第九个参数 $9 ,$0~$9 可以定义在脚本中,通过命令行向其赋值。
当向 shell 脚本传递字符串时,如果希望将多个字符串当做一个变量,需要加上单引号 ' '
.
如果脚本需要的命令行参数不止 9 个,你仍然可以处理,但是需要稍微修改一下变量名,在第 9 个变量之后,你必须在变量数字周围加上花括号,比如 ${10}
3、读取脚本名
可以使用 $0 参数获取 shell 在命令行启动的脚本名,这在编写多功能工具时很方便
(同样的脚本如果需要根据不同的脚本名执行不同的功能时,就需要用到 $0 了)
但是出现一个潜在的问题,如果使用另一个命令来执行脚本,命令会和脚本名混合在一起出现出现在 $0 参数中
这不是唯一的问题,当传给 $0 变量的实际字符串不仅仅是脚本名,而是完整的脚本路径时,变量 $0 就会使用整个路径
解决这一问题可以使用 basename 命令,它会返回不包含路径的脚本名
现在可以结和 $0 和 basename 来编写基于脚本名执行不同功能的脚本了。
4、 特殊参数变量:
$# 含有脚本运行时携带的命令行参数的个数,可以在脚本中任何地方使用这个特殊变量,就跟普通变量一样
![]()
![]()
基于 $# 实现特定功能:
![]()
![]()
5、 抓取命令行上提供的所有参数: $* $@
区别就是: $* 会将所有参数当成单个参数
$@ 会单独处理每个参数
6、移动变量:
下面这个脚本通过 shift 命令实现了使用一个参数完成多个数据的遍历,当第一个参数的长度为 0 时,循环结束。测试完第一个参数后,shift 命令会将所有的参数的位置移动一个位置。
( 要注意的是:使用 shift 命令时,一定要小心,如果某个参数被移出,它的值就被丢弃了,无法再恢复 )
![]()
留个小疑问:为什么 -n 判断 $1 是否为 0 时要加上双引号?
shift 也可以一次性移动多个位置,例如: shift 2 一次移动 2
注意:我们来看下面这个例子,我特意检测了一下当只有 一个特殊变量 $1 却有多个参数时 shift 在 while 和 case 中的具体如何工作的
此时我们在脚本中先不使用 shift,看看有多个选项时情况如何:
( 我们发现它会进行一个死循环,一直输出 - a 选项,不能够读取之后的选项 )
![]()
此时我们加上 shift,重新运行你会发现一切正常
这说明 在 while 和 case 中 ,每执行完一个选项,就需要 shift 一次,将原来的选项移出以此来读取新的选项,这对我们理解接下来的 带参数的选项很有帮助
7、 处理选项:
用一组普通的选项和参数来运行这个脚本,结果说明在处理时脚本认为所有的命令行参数都是选项
![]()
当脚本遇到双破折线时,它会停止处理选项,并将剩下的参数都当做命令行参数。
![]()
8、处理带值的选项
有的选项会带上一个额外的参数值,当命令行选项要求额外的参数时,脚本必须能检测到并正确处理。
( 这种情况下,如 $ ./testing.sh -a test1 -b -c -d test2 )
( 此处执行此脚本带有命令行参数为: ./difOption.sh -a -b test -c
如上文所说,先将 -a 存入 $1 变量,执行完 - a 选项之后执行 do 的 shift 命令将 - a 选项移出,遇到 - b 选项将其存入 $1,因为 - b 选项带有参数 test,所以需要另外准备一个变量 $2 来存入参数值 test,执行完 - b 选项之后,在 - b 选项之后执行一次 shift 将 $1 中的 - b 值移出,再在 while 循环最后执行一次 shift 将被移入 $1 的参数 test 也移出(所以此处是执行了两次 shift ),最后在将 - c 选项读入 $1 变量,自后执行完毕退出)
![]()
9、使用 getopt 命令
9.1 命令的格式
getopt 命令可以接受一系列任意形式的命令行选项和参数,并自动将它们转换成适当的格式,其命令格式如下:
getopt optstring parameters
optstring 定义了命令行有效的选项字母,还定义了哪些选项字母需要参数值,在 optstring 中列出你要在脚本中用到的每个命令行选项字母,然后,在每个需要参数值的选项字母之后加一个冒号。getopt 命令会基于你定义的 optstring 解析提供的参数
如果指定了一个不在 optstring 中的选项,默认情况下,getopt 会产生一条错误消息。
例如: $ getopt ab:cd -a -b test1 -cde test2 test3
9.2 在脚本中使用 getopt
set -- $(getopt -q ab:cd "$@")
双破折线是 set 命令的的选项之一,它会将命令行参数替换成 set 命令的命令行值,然后该方法会将原始脚本的命令行参数传给 getopt 命令,之后再将 getopt 命令的输出传给 set 命令,用 getopt 格式化后的命令行参数来替换原始的命令行参数。
getopt 命令并不擅长处理带空格和引号的参数值,它会将空格当做参数分隔符,而不是根据双引号将两者当做一个参数,我们可以使用 getopts 来解决这个问题。
10、使用 getopts 命令
每次调用 getopts 命令时,它一次只处理命令行上检测到的一个参数,处理完所有的参数后,他会推出并返回一个大于 0 的退出状态码,这让他非常适合用解析命令行所有参数的循环中。
getopts optstring variable
有效的选项字母都会列在 optstring 中,如果选项字母要求有个参数值,就加一个冒号。要去掉错误消息的话,可以在 optstring 之前加一个冒号。getopts 命令将当前参数保存在命令行中定义的 variable 中。
getopts 命令会用到两个环境变量,如果选项需要跟一个参数值,OPTARG 环境变量就会保存这个值,OPTIND 环境变量保存了参数列表中 getopts 正在处理的参数位置,这样你就能在处理完选项之后继续处理其他命令行参数了。
while 语句定义了 getopts 命令,指明了要查找哪些命令行选项,以及每次迭代中存储它们的变量名(opt),getopts 命令解析命令行选项时会移除开头的单破折线( 即运行脚本时的命令行参数的 - a 等。。),所以在 case 定义中不用单破折线
getopts 对新手来说有几个好用的功能
①可以在参数值中包括空格
②可以将选项字母和参数值放在一起使用,而不用加空格
![]()
③除此之外 getopts 还能够将命令行上找到的所有未定义的选项统一输出成问号
getopts 命令知道何时停止处理选项,并将参数留给你处理。在 getopts 处理每个选项时,它会将 OPTIND 环境变量值增一,在 getopts 完成处理时,你可以使用 shift 命令和 OPTIND 值来移动参数。
现在你就拥有了一个能在所有 shell 脚本中使用的全功能命令行选项和参数处理工具
常用的 linux 命令选项 :
选项 | 描述 |
-a | 显示所有对象 |
-c | 生成一个计数 |
-d | 指定一个目录 |
-e | 扩展一个对象 |
-f | 指定读入数据的文件 |
--help | 显示命令的帮助信息 |
-i | 忽略文本大小写 |
-l | 产生输出的长格式文本 |
-n | 使用非交互模式(批处理) |
-o | 将所有输出重定向到的指定的输出文件 |
-q | 以安静模式运行 |
-r | 递归的处理目录和文件 |
-s | 以安静模式运行 |
-v | 生成详细输出 |
-x | 排除某个对象 |
-y | 对所有问题回答 yes |
11、获得用户输入
11.1 基本的读取
read 命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后,read 命令会将数据放进一个变量。
此处的 echo 使用了 -n 选项,该选项不会在字符串末尾输出换行符,允许脚本用户紧跟其后输入数据,而不是下一行,这让脚本看起来更像表单
实际上,read 的选项 -p 也允许直接在 read 命令行指定提示符
read 命令会将提示符后输入的所有数据分配给单个变量,要么你就指定多个变量,输入的每个数据值都会分配给变量列表的下一个变量,如果变量数量不够,剩下的数据就全部分配给最后一个变量
在 read 命令行中也可以不指定变量,如果是这样,read 命令会将它收到的任何数据都放进特殊环境变量 REPLY 中,REPLY 环境变量会保存输入的所有数据,可以在 shell 脚本中像其他变量一样使用
11.2 超时
使用 read 命令时脚本很可能会一直苦等着脚本用户的输入,如果不管是否有数据输入,脚本都必须继续执行,你可以使用 - t 选项来指定一个计时器,-t 选项指定了 read 命令等待输入的秒数,当计时器过期后,read 命令会返回一个非零退出状态码
如果计时器过期,read 命令会以非零退出状态码退出,可以使用 if - then 或 while 循环这种标准的结构化语句来理清所发生的具体情况,在本例中,计数器过期时,if 语句不成立,shell 会执行 else 部分的命令
也可以让 read 命令来统计输入的字符数,当输入的字符达到预设的字符数时,就自动退出,将输入的数据赋给变量
本例中将 - n 选项和值 1 一起使用,告诉 read 命令在接受单个字符后退出,只要按下单个字符回答后,read 命令就会接受输入并将它传给变量,无需按回车键
11.3 隐藏方式读取
有时候你需要从脚本用户处获得输入,但是又不能在屏幕上显示输入信息,典型的例子就是输入密码,除此之外还有很多其他需要隐藏的数据类型。
read 命令的 - s 选项可以避免在 read 命令中输入的数据出现在显示器上(实际上,数据会被显示,只是 read 命令会将文本颜色设成跟背景色一样)
11.4 从文件读取
也可以用 read 命令来读取 linux 系统上文件里保存的数据,每次调用 read 命令,它都会从文件中读取一行文本,当文件在没有内容时,read 命令会退出并返回非零退出状态码。
最常见的搭配是对文件使用 cat 命令 ,将结果通过管道直接传给含有 read 命令的 while 命令
![]()
![]()
while 循环会持续通过 read 命令处理文件中的行,直到 read 命令以非零退出状态码退出。