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

《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 条评论
登录 后参与评论

相关文章

  • 《Linux命令行与shell脚本编程大全》第十三章 更多的结构化命令

    本章讨论bash shell的循环命令for、while和until 13.1 for命令 重复执行一系列命令在编程中很常见。 bash shell提供了for...

    xcywt
  • 《Linux命令行与shell脚本编程大全》第十一章 构建基本脚本

    11.1使用多个命令 $date;who   //  命令列表,加入分号就可以,这样会依次执行。参见5.2.1节 注意区分$(date;who),这个是进程列表...

    xcywt
  • 关于delete 和 new

    关于new和delete,有如下代码 // new_test.cpp #include<iostream> using namespace std; cl...

    xcywt
  • shell入门第五讲

    位置变量指的是脚本(或者函数)后跟的第n个参数。我们这里暂时先讨论脚本传参的用法。

    小小科
  • 仿抖音短视频APP源码,实现简单的换头像并保存

    yunbaokeji柯基
  • Python 模块:itertools

    在某些情况下,我们通常需要对序列进行一些复杂的操作,比如从序列中选出一部分元素做排列,组合,笛卡尔积等。如果自己实现这个操作未免太繁琐了,而且还会占用大量的空间...

    不可言诉的深渊
  • 使用RestTemplate消费Hybris User API获取指定用户的信息

    假设我用Hybris API调用的方式,返回Backoffice里维护的这个用户的全部信息:

    Jerry Wang
  • 最大似然估计:从概率角度理解线性回归的优化目标

    我的网站公式显示效果更好:https://lulaoshi.info/machine-learning/linear-model/maximum-likelih...

    PP鲁
  • Python爬虫进阶必备 | 关于某监测网站的内容加密分析

    还是上次的那个网站,上一篇分析了关于反爬的绕过,在正文里随口提了一句说这个网站关于正文加密的部分很简单,晚上就收到几位读者的私信,希望能够讲讲关于正文的加密是如...

    咸鱼学Python
  • 机器学习必备 | 最大似然估计:从统计角度理解机器学习

    本专栏之前的文章介绍了线性回归以及最小二乘法的数学推导过程。对于一组训练数据,使用线性回归建模,可以有不同的模型参数来描述数据,这时候可以用最小二乘法来选择最优...

    PP鲁

扫码关注云+社区

领取腾讯云代金券