《Linux命令行与shell脚本编程大全》第十四章 处理用户输入

有时还会需要脚本能够与使用者交互。bash shell提供了一些不同的方法来从用户处获得数据,

包括命令行参数命令行选项,以及直接从键盘读取输入的能力

14.1 命令行参数

就是添加在命令后的数据

比如: ./addem  10  30

10  和  30 就是传递的命令行参数

14.1.1 读取参数

bash shell会将一些称为位置参数的特殊变量分配给输入到命令行中的所有参数。

比如: $0:程序名   $1:第一个参数   $2:第2个参数 …… 以此类推直到第九个参数。

每个参数都必须用空格分开。可以使用数字也可以使用文本。如果文本有空格,需要用引号括起来,(单引号双引号均可)

若参数不止9个,第十个以后的需要加花括号处理。比如${10}:第十个参数   ${11}:第11个参数。这样就可以添加任意多的参数了。

14.1.2 读取脚本名

$0 参数获取shell在命令行启动的脚本名

也就是说 同一个脚本test。 ./test  运行  和 /xcy/home/test  这样会得到不同的结果。

basename会返回不包含路径的脚本名。比如 name=$(basename $0)

例子:

   1 #!/bin/bash
   2 # canshu test
   3 echo "Command Name is $0"
   4 name=$(basename $0)
   5 echo "file name is $name"
   6 echo "Hi, My name is $1, I am $2, I am come from $3"
   7 echo "Param4: $4,  Param5:$5 ......  Param10:${10}, Param11:${11}"  

运行:

14.1.3 测试参数

如果脚本中使用了命令行参数,脚本不加参数运行可能会出问题。

可以先判断参数是否存在。使用前此案检查是否存在数据

使用 –n 来检查命令行参数是否有数据。

例子:

   1 #/bin/bash
   2 # check param
   3 if [ -n "$1" ]  # 需要用双引号括起来
   4 then
   5         echo "hello, $1, nice to meet you"
   6 else
   7         echo "sorry, you are illegality"
   8 fi

运行:可以这样

./test2

./test2 xiaochongyong

14.2 特殊参数变量

14.2.1 参数统计

$# 可以统计参数个数,统计输入了多少个参数

假如有5个参数,那么 $# 会是5。 那么${$#}就应该是最后一个参数。然而其实并不是这样。

其实不能在花括号里面是有美元符。必须换成感叹号。${!#}

14.2.2 抓取所有的变量

抓取命令行上提供的所有参数。

$* 和 $@ 变量可以用来访问所有的参数。

$*:将命令行上提供的所有参数当做一个单词保存,可以看做一个整体

$@:将命令行上提供的所有参数当做同一字符串中的多个独立的单词。可以用for遍历所有的参数值。

例子: 这里的反斜杠是为了转义美元符

   1 #!/bin/bash
   2 # test
   3 echo "Param count:$#"
   4 echo "Last Param:${!#}"
   5 echo "param \$*:$*"
   6 echo "param \$@:$@"
   7 count=1
   8 for param in "$*"  # for param in $*, no ""。如果没有双引号,效果一样
   9 do
  10         echo "  Param \$* #$count = $param"
  11         count=$[ $count + 1 ]
  12 done
  13 
  14 count=1
  15 for param1 in "$@"
  16 do
  17         echo "  Param \$@ #$count = $param1"
  18         count=$[ $count + 1 ]
  19 done

运行:./test 1 2 3 4 5 

注意第10行和第17行的输出的区别

14.3 移动变量

bash shell的shift工具命令能操作命令行参数。

shift命令会根据它们的相对位置来移动命令行参数。

如何使用:默认情况下将每个参数变量向左移动一个位置。

$3移动到$2, $2移动到$1。$1的值就被丢弃了,$0的值不会被改变。

   1 #!/bin/bash
   2 # shift test
   3 count=1
   4 while [ -n "$1" ]
   5 do
   6         echo "Param$count = $1"
   7         count=$[ $count + 1 ]  # 这个是数学运算
   8         shift
   9 done
 

运行: ./test 1 2 3 4 5 6 7

还可以一次性移动多个, shift n  n表示移动位置数

14.4 处理选项

选项:跟在单破折线后面的单个字母,它能改变命令的行为。比如 ls -a

14.4.1 查找选项

1.处理简单选项。

比如下面的例子:

   1 #!/bin/bash
   2
   3 if [ -n "$1" ]
   4 then
   5         case "$1" in
   6         -a) echo "this is -a option";;
   7         -b) echo "this is -b option";;
   8         -c) echo "this is -c option";;
   9         -d) echo "this is -d option";;
  10         esac
  11 else
  12         echo "no param"
  13 fi 

运行:

./test -a

./test –b

2. 分离参数和选项

如果需要在shell脚本中同时使用选项和参数。

解决方案是用特殊字符来将二者分开。

shell会用双破折线来表明选项列表结束。在双破破折现之后脚本就可以放心的将剩下的命令当做参数了。而不是选项

运行脚本时, -- 之前的是选项和选项的参数。  -- 的之后就是命令行参数。

3. 处理带值的参数

有时候选项会带上一个额外的参数值。

由于要处理的选项是$1 ,那么参数就在它后面,就是$2.

例子:

   1 #!/bin/bash
   2 echo
   3 while [ -n "$1" ]
   4 do
   5         case "$1" in
   6         -a) echo "Found the -a option";;
   7         -b) value=$2  # 这里处理选项参数
   8                 echo "Found the -b option, Param = $value"
   9                 shift;;
  10         -c) echo "Found the -c option";;
  11         --) shift
  12                 break;;
  13         *) echo "$1 is not option";;
  14         esac
  15         shift
  16 done
  17 # 上面处理选项,下面是处理命令行参数
  18 count=1
  19 for param in $@
  20 do
  21         echo "Param$count = $param"
  22         count=$[ $count + 1 ]
  23 done

运行:

14.4.2 使用getopt命令

它能够识别命令行参数,从而在脚本中解析它们。

1.命令的格式

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

命令格式如下:

getopt optstring parameters

optstring:定义了命令行有效的选项字母,还定义了哪些选项字母需要参数值

比如:

$getopt ab:cd –a –b testb –cd test2 test3

b后面接了冒号:说明-b选项需要参数。

后面 –cd 会被自动拆分成 –c  -d 两个单独的选项

还会插入双破折线来分割行中的额外参数。

2.在脚本中使用getopt

可以在脚本中使用getopt来格式化脚本所携带的任何命令行选项或者参数,用起来略繁琐。

方法是:用getopt命令生成的格式化后的版本来替换已有的命令行参数和选项。

用set命令可以做到。需要的选项之一就是 双破折号 --。它会将命令行参数替换成set命令的命令行值。

但是这个不擅长处理带空格和带引号的参数值,它会将空格当做参数分隔符,而不是根据双引号将二者当做一个参数

例子:

   1 #!/bin/bash
   2 echo
   3 set -- $(getopt -q ab:cd: "$@")
   4 while [ -n "$1" ]
   5 do
   6         case "$1" in
   7         -a) echo "Found the -a option";;
   8         -b) value=$2
   9                 echo "Found the -b option, Param = $value"
  10                 shift;;
  11         -c) echo "Found the -c option";;
  12         -d) value=$2
  13                 echo "Found the -d option, Param = $value"
  14                 shift;;
  15         --) shift
  16                 break;;
  17         *) echo "$1 is not option";;
  18         esac
  19         shift
  20 done
  21 
  22 count=1
  23 for param in $@
  24 do
  25         echo "Param$count = $param"
  26         count=$[ $count + 1 ]
  27 done

运行:

3. 使用更高级的getopts

getopt将命令行上选项和参数的处理结后只生成一个输出。

getopts能够和已有的shell参数变量配合默契。

每次调用它时,它一次只处理命令行上检测到的一个参数。处理完所有的参数后,会退出并返回一个大于0的退出状态码。这样就可以循环了。

格式如下:

getopts optstring variable

如果选项有参数就在后面加冒号

要去掉错误消息就在前面加冒号。

getopts会用到两个环境变量:

OPTARG:会保存一个参数值

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

getopts知道何时停止处理选项,并且把参数留给你处理。

每处理一个选项,OPTIND就会增1,处理完选项时就可以用shift来移动参数。从而处理参数。

例子:

   1 #!/bin/bash
   2 echo
  3 while getopts :ab:c opt
   4 do
   5         case "$opt" in
   6         a) echo "Found -a option";;
   7         b) echo "Found -b option, with value:$OPTARG";;
   8         c) echo "Found -c option";;
   9         *) echo "Unkonw option: $opt"
  10         esac
  11 done
  12 
  13 echo
  14 echo "OPNTING = $OPTIND"
  15 shift $[ $OPTIND - 1 ]  #这里开始处理参数,先移动参数
  16 count=1
  17 for param in "$@"
  18 do
  19         echo "Param$count = $param"
  20         count=$[ $count + 1 ]
  21 done

运行:

14.5 将选项标准化

有一些选项是标准化了,约定俗成的东西。自己写的脚本中可以支持这些选项。这样用户使用你的脚本就不用查手册了。

比如:

-a 显示所有对象

-c 生成一个计数

-h 显示命令的帮助信息

-I 忽略文本大小写

-v 生成详细输出

……

14.6 获得用户输入

使用场景:脚本运行时可以问个问题,并等待运行脚本的人来回答。

为此提供了read命令

14.6.1 基本的读取

read命令从标准输入或另外一个文件描述符中接收输入。收到输入后read将数据放进一个变量中去。

read 还可以接 –p选项,允许在read命令行指定提示符。

还可以指定多个变量,如果变量数量不够,那么剩下的数据就全部分配到最后一个变量中。

如果没有指定变量名,会放到环境变量REPLY中。

例子:

   1 #!/bin/bash
   2 echo -n  "Please input you name:"
   3 read name
   4 echo "Hello $name, welcome to xcy program"
   5 read -p "Please input you age: " age  # 指定提示符
   6 echo "you are live:$[ $age * 365 ] days"
   7 read -p "step2 Please input you company and num:" company num  # 多个变量
   8 echo "you company:$company, num:$num"
   9 read -p "REPLY test:" # 不值得变量名
  10 echo "You input REPLY = $REPLY"

运行:

14.6.2 超时

如果使用了read,脚本会一直等待用户输入。

如果不管脚本是否有数据输入都继续执行下去,就可以指定一个定时器。

-t 指定一个计时器,指定了read命令等待输入的秒数。定时器过期后,read返回一个非0退出状态码。

还可以不对输入过程计时,而是统计字数,字数到了就自动退出,将输入数据赋给变量。

用-n选项,后面接字数

例子:

   1 #/bin/bash
   2 if read -t 10 -p "Input you name:" name  # 计时
   3 then
   4         echo "Hi $name, welcome"
   5 else
   6         echo
   7         echo "sorry, to slow"
   8 fi
   9 
  10 read -n1 -p "Do you want to continue [Y/N]? " answer # 限定字符
  11 case $answer in
  12 Y) echo
  13 echo "you are continue ......";;
  14 N) echo
  15 echo "Bye Bye!!!";;
  16 *) echo
  17 echo "Error, Bye";;
  18 esac

14.6.3 隐藏方式读取

比如输入密码,不想别人看见,就需要隐藏输入。

加 –s 选项

比如:

   1 #!/bin/bash
   2 read -s -p "input you Pass:" Pass
   3 echo
   4 read -s -p "input you Pass again:" Pass1
   5 echo
   6 if [ $Pass = $Pass1 ]  # 字符串比较是否相同
   7 then
   8         echo "Success!!!"
   9 else
  10         echo "Falied, Two inconsistent"
  11 fi 

14.6.4 从文件中读取

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

难点在于将文件中的数据传给read命令。最常见的方法是对文件使用cat命令,将结果通过管道直接传给含有read命令的while命令。

例子:

   1 #!/bin/bash
   2 read -p "Please input you read file name:" name
   3 if [ -e $name ]  # 是否存在
   4 then
   5         count=1
   6         cat $name | while read line     
   7         do
   8                 echo "Line $count:$line"
   9                 count=$[ $count + 1 ]
  10         done
  11 else
  12         echo "$name not exist"
  13 fi

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏

RoR: Ruby On Rails 的 Web Service 2 使用before_invocation进行验证调用权限

class BlogApi < ActionWebService::API::Base   api_method :hello_world,   :expect...

9530
来自专栏大内老A

使命必达: 深入剖析WCF的可靠会话[协议篇](下)

在《上篇》中,我们认识了从序列创建到终止过程中消息交换的大致流程。接下来,我们进一步将关注点聚焦到单个小消息上,看看在整个基于序列的上下文中,不同类型的消息具有...

25280
来自专栏大内老A

从数据到代码——基于T4的代码生成方式

在之前写一篇文章《从数据到代码》(上篇、下篇)中,我通过基于CodeDOM+Custom Tool的代码生成方式实现了将一个XML表示的消息列表转换成了相应的C...

31880
来自专栏抠抠空间

Flask之WTForms

WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。

12030
来自专栏代码世界

Flask之wtforms

WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。

15430
来自专栏码神联盟

面试题 | 《Java面试题集》-- 第三套

varchar2分别在oracle的sql和pl/sql中都有使用,oracle 在sql参考手册和pl/sql参考手册中指出:oracle sql varch...

17120
来自专栏小白的技术客栈

两个微型的函数例子

hello小伙伴们大家好,还记得昨天的函数参数的文章吗?你get到了多少呢?实际的工作中并没有那么复杂,一个通用的函数定义形如: def func_name(*...

34450
来自专栏Java技术栈

史上最全Java多线程面试题及答案

多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域。所以,学好多线程并发编程对Java程序员来来说极其重要的。 下面小编整理了60道最常见的Ja...

440110
来自专栏Jimoer

JVM学习记录-类加载器

JVM设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外面去实现,以便让应用程序自己决定如何去获取所需要的...

8210
来自专栏Python

wtforms

 简介 WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。 安装: ? 1 pip3 install wtf...

23870

扫码关注云+社区

领取腾讯云代金券