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


相关文章
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
1月前
|
Shell
一个用于添加/删除定时任务的shell脚本
一个用于添加/删除定时任务的shell脚本
86 1
|
2月前
|
Shell Linux
Linux shell编程学习笔记30:打造彩色的选项菜单
Linux shell编程学习笔记30:打造彩色的选项菜单
|
25天前
|
Shell Linux 测试技术
6种方法打造出色的Shell脚本
6种方法打造出色的Shell脚本
48 2
6种方法打造出色的Shell脚本
|
11天前
|
XML JSON 监控
Shell脚本要点和难点以及具体应用和优缺点介绍
Shell脚本在系统管理和自动化任务中扮演着重要角色。尽管存在调试困难、可读性差等问题,但其简洁高效、易于学习和强大的功能使其在许多场景中不可或缺。通过掌握Shell脚本的基本语法、常用命令和函数,并了解其优缺点,开发者可以编写出高效的脚本来完成各种任务,提高工作效率。希望本文能为您在Shell脚本编写和应用中提供有价值的参考和指导。
33 1
|
16天前
|
Ubuntu Shell 开发工具
ubuntu/debian shell 脚本自动配置 gitea git 仓库
这是一个自动配置 Gitea Git 仓库的 Shell 脚本,支持 Ubuntu 20+ 和 Debian 12+ 系统。脚本会创建必要的目录、下载并安装 Gitea,创建 Gitea 用户和服务,确保 Gitea 在系统启动时自动运行。用户可以选择从官方或小绿叶技术博客下载安装包。
37 2
|
30天前
|
监控 网络协议 Shell
ip和ip网段攻击拦截系统-绿叶结界防火墙系统shell脚本
这是一个名为“小绿叶技术博客扫段攻击拦截系统”的Bash脚本,用于监控和拦截TCP攻击。通过抓取网络数据包监控可疑IP,并利用iptables和firewalld防火墙规则对这些IP进行拦截。同时,该系统能够查询数据库中的白名单,确保合法IP不受影响。此外,它还具备日志记录功能,以便于后续分析和审计。
49 6
|
26天前
|
运维 监控 Shell
深入理解Linux系统下的Shell脚本编程
【10月更文挑战第24天】本文将深入浅出地介绍Linux系统中Shell脚本的基础知识和实用技巧,帮助读者从零开始学习编写Shell脚本。通过本文的学习,你将能够掌握Shell脚本的基本语法、变量使用、流程控制以及函数定义等核心概念,并学会如何将这些知识应用于实际问题解决中。文章还将展示几个实用的Shell脚本例子,以加深对知识点的理解和应用。无论你是运维人员还是软件开发者,这篇文章都将为你提供强大的Linux自动化工具。
|
1月前
|
Shell
Shell编程(下)
Shell编程(下)
92 1
|
1月前
|
Shell Linux Windows
Shell编程(上)
Shell编程(上)
41 1