shell脚本编程之处理用户输入(二)

简介: shell脚本编程之处理用户输入(二)

7.处理选项


选项是跟在单折线后面的单个字母,它能改变命令的行为。下面将介绍3种在脚本中处理选项的方法。


7.1 查找选项


处理简单选项:在提取每个单独参数时,用case语句来判断某个参数是否为选项。如下例所示:


#!/bin/bash
while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option";;
        -b) echo "Found the -b option";;
        -c) echo "Found the -c option";;
        *) echo "$1 is not an option";;
    esac
    shift
done
# 结果
[njust@njust tutorials]$ ./bar16.sh -a -b -c -d
Found the -a option
Found the -b option
Found the -c option
-d is not an option


分离参数和选项:在shell脚本中同时使用选项和参数的情况,Linux中处理这个问题的标准方法是用特殊字符来将两者分开,该字符会告诉脚本何时选项结束以及普通参数何时开始。对Linux来说,这个特殊字符是--,shell会用双破折线来表明选项列表已经结束。在双破折线后,脚本就可以放心将剩下的命令行参数当作参数,而不是选项。如下例所示:


#!/bin/bash
while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option";;
        -b) echo "Found the -b option";;
        -c) echo "Found the -c option";;
        --) shift
            break;;
        *) echo "$1 is not an option";;
    esac
    shift
done
count=1
for param in $@
do
    echo "Parameter #$count: $param"
    count=$[ count + 1 ]
done
# 结果
[njust@njust tutorials]$ ./bar17.sh -c -a -b -- test1 test2 test3
Found the -c option
Found the -a option
Found the -b option
Parameter #1: test1
Parameter #2: test2
Parameter #3: test3


处理带值的选项:有些选项会带上一个额外的参数值,如./bar666.sh -a test1 -b -c -d test2。当命令行选项要求额外的参数时,脚本必须能检测到并正确处理。如下例所示:


#!/bin/bash
while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option";;
        -b) param="$2"
            echo "Found the -b option,with parameter value $param."
            shift;;
        -c) echo "Found the -c option";;
        --) shift
            break;;
        *) echo "$1 is not an option";;
    esac
    shift
done
count=1
for param in "$@"
do
    echo "Parameter #$count: $param"
    count=$[ $count + 1 ]
done
# 结果
[njust@njust tutorials]$ ./bar19.sh -a -b test1 -f -- demo1 demo2
Found the -a option
Found the -b option,with parameter value test1.
-f is not an option
Parameter #1: demo1
Parameter #2: demo2

7.2 使用getopt命令


getopt命令是一个在处理命令行选项和参数时非常方便的工具,它能识别命令行参数,从而在脚本中解析它们时更方便。getopt命令可以接受一系列任意形式的命令行选项和参数,并自动转换成适当的格式,具体命令格式如下所示:


getopt optstring parameters


optstring定义了命令行有效的选项字母,还定义了哪些选项字母需要参数值。optstring中列出了要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后加一个冒号。注意:getopt命令有一个更高级的版本即getopts,注意两者的区别。具体实例如下所示:


[njust@njust tutorials]$ getopt ab:cd -a -b test1 -cd test2 test3
-a -b test1 -c -d -- test2 test3


上述示例中,optstring定义了四个有效选项字母:a、b、c、d。字母b后面的冒号表示b选项需要一个参数值。注意:getopt命令会将-cd选项分成两个单独的选项,并插入双破折线来分隔行中的额外参数。如果指定了一个不在abcd选项中的参数,默认情况下,getopt命令会产生一条错误的信息。如下所示:


[njust@njust tutorials]$ getopt ab:cd -a -b test1 -cdf test2 test3
getopt:无效选项 -- f
-a -b test1 -c -d -- test2 test3


在脚本文件中使用getopt:用getopt命令生成的格式后的版本来替换自己已有的命令行选项和参数,用set命令能够做到。set命令使用的方法如下


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


利用上述方法,可以帮我们处理命令行参数的脚本。如下例所示:


#!/bin/bash
set -- $(getopt -q ab:cd "$@")   # 注意此条语句!!!
while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option";;
        -b) param="$2"
            echo "Found the -b option,with parameter value $param."
            shift;;
        -c) echo "Found the -c option";;
        --) shift
            break;;
        *) echo "$1 is not an option";;
    esac
    shift
done
count=1
for param in "$@"
do
    echo "Parameter #$count: $param"
    count=$[ $count + 1 ]
done
# 结果
[njust@njust tutorials]$ ./bar20.sh -ac  # 合并选项情况下也能处理!
Found the -a option
Found the -c option


但是,getopt命令也存在一个小问题它并不擅长处理带空格和引号的参数值。它会将空格当成参数分隔符,而不是根据双引号将两者看成一个参数,解决方法是使用更高级的getopts命令。


[njust@njust tutorials]$ ./bar20.sh -a -b test1 -c "test2 test3" test4
Found the -a option
Found the -b option,with parameter value 'test1'.
Found the -c option
Parameter #1: 'test2
Parameter #2: test3'
Parameter #3: 'test4'

7.4 更高级的getopts命令


getopts命令是shell中的内建命令,它比getopt命令多了一些扩展功能。getopt命令将命令行上选项和参数处理后只生成一个输出,而getopts命令能够和已有的shell参数变量配合使用。每次调用它时,它一次只处理命令行上检测到的一个参数,处理完所有的参数后,它会退出并返回一个大于0的退出状态码,这让它非常适合用解析命令行所有参数的循环中。getopts命令的格式如下:


getopts optstring variable

optstring值类似于getopt命令中的那个,getopts命令将当前参数保存在命令行中定义的variable中。getopts命令会用到两个环境变量,如果选项需要跟一个参数值,OPTARG环境变量就会保存这个值,OPTIND环境变量保存了参数列表中getopts正在处理的参数位置,这样你就能在处理完选项后继续处理其他命令行参数了。如下例所示:


#!/bin/bash
while getopts :ab:c opt
do
    case "$opt" in
        a) echo "Found the -a option";; # 注意:getopts命令在解析命令行选项时会移除开头的单破折线,所以在case定义中不用单破折线!
        b) echo "Found the -b option,with value $OPTARG";;
        c) echo "Found the -c option";;
        *) echo "Unknown option: $opt";;
    esac
done
# 结果
[njust@njust tutorials]$ ./bar21.sh  -ab test1 -c
Found the -a option
Found the -b option,with value test1
Found the -c option


getopts命令的优点1:可以在参数值中包含空格,如下例所示:


[njust@njust tutorials]$ ./bar21.sh  -b "test1 test2" -a
Found the -b option,with value test1 test2
Found the -a option


getopts命令的优点2:是将选项字母和参数值放在一起使用,而不用加空。如下例所示:


[njust@njust tutorials]$ ./bar21.sh  -abdemo1
Found the -a option
Found the -b option,with value demo1


此外,getopts命令还可以将命令行上找到的所有未定义的选项统一输出为问号,如下例所示:


[njust@njust tutorials]$ ./bar21.sh  -acdf
Found the -a option
Found the -c option
Unknown option: ?
Unknown option: ?


getopts命令知道何时停止处理选项,并将参数留给你处理。在getopts处理每个选项时,它会将OPTIND环境变量值加1,在getopts完成处理时,使用shift命令和OPTIND值来移动参数。如下例所示:


#!/bin/bash
while getopts :ab:cd opt
do
    case "$opt" in
        a) echo "Found the -a option";;
        b) echo "Found the -b option,with value $OPTARG";;
        c) echo "Found the -c option";;
        d) echo "Found the -d option";;
        *) echo "Unknown option: $opt";;
    esac
done
shift $[ $OPTIND - 1 ]
count=1
for param in "$@"
do
    echo "Parameter $count:$param"
    count=$[ $count + 1 ]
done
# 结果
[njust@njust tutorials]$ ./bar22.sh -a -b test1 -d test2 test3 test4
Found the -a option
Found the -b option,with value test1
Found the -d option
Parameter 1:test2
Parameter 2:test3
Parameter 3:test4


8.选项标准化


下面是Linux中常用到的一些命令行选项的含义:


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


9.获取用户输入


尽管命令行选项和参数是从脚本用户处获得输入的一种重要方式,但是有时候脚本的交互性还需要更强一些。例如在运行脚本时问一个问题,并等待运行脚本的人来回答,bash shell为此提供了read命令


9.1 基本的读取


read命令从标准输入或另一个文件描述符中接收输入。在收到输入后,read命令会将数据放进一个变量,如下例所示:


#!/bin/bash
echo -n "Please Enter your name: "  # -n选项不会在字符串末尾输出换行符,允许脚本用户紧跟其后输入数据,而不是在下一行输入!
read name
echo "Hello $name,welcome to my home."
# 结果
[njust@njust tutorials]$ ./bar23.sh 
Please Enter your name: Curry   # 自己输入的!
Hello Curry,welcome to my home.


read命令包含了-p选项,它允许你直接在read命令行指定提示,如下例所示:


#!/bin/bash
read -p "Please Enter your age: " age
days=$[ $age * 365 ]
echo "That makes you over $days days old"
# 结果
[njust@njust tutorials]$ ./bar24.sh 
Please Enter your age: 28
That makes you over 10220 days old


也可以在read命令行中不指定变量,read命令会将它收到的任何数据都放进特殊环境变量REPLY中,REPLY环境变量会保存输入的所有数据,可以在shell脚本中像其他变量一样使用。如下例所示:


#!/bin/bash
read -p "Enter your name: "
echo "Hello $REPLY,welcome to NewYork."
# 结果
[njust@njust tutorials]$ ./bar25.sh 
Enter your name: Stephen Curry
Hello Stephen Curry,welcome to NewYork.


9.2 超时


使用read命令时一定要注意,脚本很可能会一直等待用户的输入。如果不管是否有数据输入,脚本都必须继续执行,可以使用-t选项来指定一个计数器。-t选项指定了read命令等待输入的秒数,当计数器超时后,read命令会返回一个非零退出状态码,如下例所示:


#!/bin/bash
if read -t 5 -p "Please enter your name: " name
then
    echo "Hello $name,welcome to NewYork."
else
    echo 
    echo "Sorry, it's wrong!"
fi
# 结果
[njust@njust tutorials]$ ./bar26.sh 
Please enter your name: 
Sorry, it's wrong!


也可以不对输入过程计时,而是让read命令来统计输入的字符数,当输入的字符达到预设的字符数时,就自动退出,将输入的数据赋值给变量。如下例所示:


#!/bin/bash
read -n1 -p "Do you want to continue [Y/N]? " answer
case $answer in
    Y | y) echo
            echo "Fine,continue on...";;
    N | n) echo
            echo "Ok,goodbye"
            exit;;
esac
echo "This is the end of the script"
# 结果
[njust@njust tutorials]$ ./bar27.sh 
Do you want to continue [Y/N]? Y
Fine,continue on...
This is the end of the script


上述示例代码段中,-n选项与值1一起使用,它告诉read命令在接收单个字符后退出。只要按下单个字符后,read命令就会接收输入并将它传给变量,无需按下回车键。



9.3 隐藏方式读取


有时候需要从用户输入处得到输入,但又不能在屏幕中显示输入。其中典型的例子就是输入的密码,此外还有很多其他需要隐藏的数据类型。-s选项可以避免在read命令中输入的数据出现在屏幕上,如下例所示:


#!/bin/bash
read -s -p "Enter you password: " passwd
echo
echo "Is your password really $passwd? "
# 结果
[njust@njust tutorials]$ ./bar28.sh 
Enter you password: 
Is your password really 12345?


9.4 从文件中读取


可以用read命令来读取Linux系统上文件中保存的数据,每次调用read命令,它都会从文件中读取一行文本。当文本中没有内容时,read命令会退出并返回非零退出状态码。注意:最难的部分是将文件中的数据传给read命令,最常见的方法是对文件使用cat命令,将结果通过管道直接传给含有read命令的while命令。如下例所示:


#!/bin/bash
count=1
cat demodemo | while read line
do
    echo "Line $count: $line"
    count=$[ $count + 1 ]
done
echo "Finished processing the file"
# 结果
[njust@njust tutorials]$ ./bar29.sh 
Line 1: The quick brown dog jumps over the lazy fox.
Line 2: This is a test, this is only a test.
Line 3: O Romeo, Romeo!Wherefore art thou Romeo?
Finished processing the file


相关文章
|
15天前
|
Shell
Shell脚本有哪些基本语法?
【9月更文挑战第4天】
36 17
|
15天前
|
存储 Unix Shell
shell脚本编程基础
【9月更文挑战第4天】
28 12
|
14天前
|
网络协议 关系型数据库 MySQL
Shell 脚本案例
Shell 脚本案例
28 8
|
14天前
|
Shell Linux
Shell 编程 编写hello word
Shell 编写hello word
36 5
|
14天前
|
Shell Linux 开发工具
linux shell 脚本调试技巧
【9月更文挑战第3天】在Linux中调试shell脚本可采用多种技巧:使用`-x`选项显示每行命令及变量扩展情况;通过`read`或`trap`设置断点;利用`echo`检查变量值,`set`显示所有变量;检查退出状态码 `$?` 进行错误处理;使用`bashdb`等调试工具实现更复杂调试功能。
|
29天前
|
Shell KVM 虚拟化
Shell 数组编程
【8月更文挑战第22天】 Shell 数组编程
39 10
|
30天前
|
Ubuntu Linux Shell
在Linux中,如何使用shell脚本判断某个服务是否正在运行?
在Linux中,如何使用shell脚本判断某个服务是否正在运行?
|
28天前
|
Java Shell Linux
【Linux入门技巧】新员工必看:用Shell脚本轻松解析应用服务日志
关于如何使用Shell脚本来解析Linux系统中的应用服务日志,提供了脚本实现的详细步骤和技巧,以及一些Shell编程的技能扩展。
22 0
【Linux入门技巧】新员工必看:用Shell脚本轻松解析应用服务日志
|
29天前
|
监控 Shell Linux
在Linux中,如何使用shell脚本进行系统监控和报告?
在Linux中,如何使用shell脚本进行系统监控和报告?
|
30天前
|
Shell Linux
在Linux中,shell脚本中的条件语句和循环结构是什么?
在Linux中,shell脚本中的条件语句和循环结构是什么?