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

目录

  • 3.3 处理用户输入
    • 3.3.1 命令行参数
      • 1.位置参数
      • 2.对参数进行测试
    • 3.3.2 特殊参数变量
      • 1.参数个数
      • 2.抓取所有的数据
    • 3.3.3 移动变量
    • 3.3.4 处理选项
      • 1. 处理简单的选项
      • 2. 分离参数和选项
      • 3. 更高级的选项处理方法
    • 3.3.5 业内统一的选项含义
    • 3.3.6 获取用户输入-read
      • 1.read的基本使用
      • 2.超时管理
      • 3.不显示读取内容
      • 4.从文件中读取内容

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会读取一行内容。

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

  1. Linux之shell编程篇-----Shell 函数和Shell 输入/输出重定向

    ​ 最近发现了一个挺厉害的人工智能学习网站,内容通俗易懂,风趣幽默,感兴趣的可以点击此链接进行查看:床长人工智能教程 废话不多说,请看正文! ​ Shell 函数 1. 自定义函数 linux she ...

  2. (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹

    (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹 原文 (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹 (本系列文章由柠檬的(lc_mtt)原创,转载 ...

  3. shell编程-初始shell及变量

    shell编程 初始shell 程序 语言 编程 ---------------------------------- 语言 自然语言:汉语.英语 计算机语言:c语言.c++.(java php py ...

  4. Shell编程入门-Shell程序设计实例

    Shell编程入门-Shell程序设计实例 1.增加用户账户 编写一个Shell程序addaccount.sh,在系统中增加四个账户,同时设定他们的初始密码为123456,主组群为wl20. ①查看系 ...

  5. SHELL编程——初识SHELL编程

    一.什么是SHELL编程? Shell是操作系统的最外层,Shell可以合并编程语言以控制进程和文件,以及启动和控制其他程序.Shell通过提示您输入,向操作系统解释该输出,然后处理来自操作系统的任何 ...

  6. 8、python基础知识-用户输入

    #!/usr/bin/env python # _*_ coding: utf-8 _*_ num = 100 name = input("your name:") age = i ...

  7. Java黑皮书课后题第4章:*4.18(学生的专业和年级)编程一个程序,提示用户输入两个字符,显示这两个字符代表的专业以及年级,第一个字符表示专业,第二个是一个数字字符1、2、3、4,输出对应结果

    *4.18(学生的专业和年级)编程一个程序,提示用户输入两个字符,显示这两个字符代表的专业以及年级,第一个字符表示专业,第二个是一个数字字符1.2.3.4,分别表示大一.大二.大三.大四 题目 题目概 ...

  8. Linux中shell编程——编写shell脚本,实现功能:在用户输入年、月后自动打印出对应年月的日

    实验目的 理解shell程序的设计方法 掌握shell程序编辑.运行.调试方法与过程 实验内容 shell简单编程 实验过程 要求: 1.记录实验过程,对关键过程进行文字说明并附上操作截屏. 2.逻辑 ...

  9. shell编程(六) : [shell基础] 基本shell脚本

    接上一篇文章Linux shell编程(五): Linux文件权限管理 三.Linux shell 脚本编程基础 了解了Linux系统和命令行的基础知识,是时候开始编程了. 3.1 基本shell脚本 ...

最新文章

  1. python使用imbalanced-learn的NeighbourhoodCleaningRule方法进行下采样处理数据不平衡问题
  2. Python操作数据库之 MySQL
  3. VALSE 视觉资源汇总(视频+PPT+文章解读,持续更新)
  4. 使用javassist动态注入代码
  5. java 动态代理 阿bin_Android WebView 的方法只能在 UI 线程中运行
  6. Docker学习总结(58)——Dockerfile中,ADD和COPY的区别?
  7. this.$set 更新整个数组_学点算法(二)——有序数组二分搜索算法
  8. 五年级数学用计算机找规律教案,五年级数学教案——找规律教学设计
  9. mysql的驱动connect放在哪里_MySQL的JDBC驱动程序下载地址
  10. Tomcat 9.0 下载安装 配置
  11. 图形的装饰教案计算机,《有趣的图形》中班教案
  12. phpmyadmin mysql_phpmyadmin
  13. JS旋转木马图片轮播
  14. c语言ans作用,ANS标准定义C语言是什么?
  15. dns配置异常怎么修复_电脑出现dns错误不能上网怎么办?dns错误修复方法
  16. 华为p4不是鸿蒙吗怎么又改为安卓_华为鸿蒙系统不是为华为手机设计的 华为手机将继续使用安卓...
  17. 深度学习服务器配置过程
  18. mysql语句大全_MySQL语句大全
  19. 壁纸搜索系统/壁纸管理系统的设计与实现
  20. tensorflow tutorials(一):用tensorflow建立线性回归模型

热门文章

  1. 洋桃开发板笔记(一) 最基本的GPIO接口控制
  2. android 线程间通信几种方式
  3. OpenCV(三)——图像分割
  4. 多线程编程定长线程池
  5. 苏州太仓|第六届“创赢太仓”全球创业大赛“上海城市赛”
  6. 脑机接口专栏 | 如何分析静息状态的fMRI数据?(三)
  7. 在LinuxMint19上安装搜狗拼音输入法
  8. PTA----神坛 (30分)(计算几何)
  9. 线上RPC超时故障排查及后续GC调优思路
  10. Linux使用sed命令删除文件中指定内容