【Linux命令行与Shell脚本编程】十三,Linux处理用户输入

Linux命令行与Shell脚本编程

第十三章 处理用户输入



三.处理用户输入

1,传递参数

向 shell脚本传递数据的最基本方法是使用命令行参数。
命令行参数允许运行脚本时在命令行中添加数据:
    $ ./addem 10 30

如何在bash shell脚本中使用命令行参数?

1.1,读取参数 $1~9 ${10~n}

bash shell会将所有的命令行参数都指派给称作 位置参数。这也包括shell脚本名称。

位置变量 的名称都是标准数字:$0对应脚本名,$1对应第一个命令行参数,$2对应第二个命令行参数,以此类推,直到$9.参数之间必须用空格分开.
    $ cat positional1.sh
        #!/bin/bash
        echo $1
        echo $2
    $ ./positional1.sh 1 ok
       5
       ok
带空格的参数需要使用 "" 或 ''.

如果脚本需要的命令行参数不止9个,仍可以继续加入更多的参数,但在之后,必须在变量名两侧加上花括号,比如${10}。

1.2,读取脚本名 basename $0

使用位置变量$0获取在命令行中运行的shell脚本名。
拥有一个能 识别自己 的脚本对于追踪脚本问题、系统审计和生成日志信息是非常有用的。
    $ cat positional0.sh
        #!/bin/bash
        echo This script name is $0.
        exit
    $ bash positional0.sh
        This script name is positional0.sh.
如果使用另一个命令来运行shell脚本,则命令名会和脚本名混在一起,出现在位置变量$0中:
    $ ./positional0.sh
        This script name is ./positional0.sh.
如果运行脚本时使用的是绝对路径,那么位置变量$0就会包含整个路径:
    $ $HOME/scripts/positional0.sh
        This script name is /home/christine/scripts/positional0.sh.
    
basename命令可以返回不包含路径的脚本名:
    $ cat posbasename.sh
        #!/bin/bash
        name=$(basename $0)
        echo This script name is $name.
        exit
    $ ./posbasename.sh
        This script name is posbasename.sh.

1.3,参数测试 if [ -n “$1” ]

在shell脚本中使用命令行参数时要当心。如果运行脚本时没有指定所需的参数,则可能会出问题.

当脚本认为位置变量中应该有数据,而实际上根本没有的时候,脚本很可能会产生错误消息。
在使用位置变量之前一定要检查是否为空:
    $ cat checkpositional1.sh
        #!/bin/bash
        if [ -n "$1" ]
        then
             factorial=1
             for (( number = 1; number <= $1; number++ ))
             do
                  factorial=$[ $factorial * $number ]
             done
             echo The factorial of $1 is $factorial
        else
             echo "You did not provide a parameter."
        fi
        exit
    $ ./checkpositional1.sh
        You did not provide a parameter.
    $ ./checkpositional1.sh 3
        The factorial of 3 is 6

2,特殊参数变量

跟踪命令行参数的 特殊变量。

2.1,参数统计 $# ${!#}

在脚本中使用命令行参数之前应该检查一下 位置变量。对使用多个命令行参数的脚本来说很麻烦

统计一下有多少个命令行参数。bash shell为此提供了一个特殊变量。

特殊变量$#含有脚本运行时携带的命令行参数的个数。跟普通变量一样可以在脚本中的任何地方使用这个特殊变量.
    $ cat addem.sh
        #!/bin/bash
        if [ $# -ne 2 ]
        then
             echo Usage: $(basename $0) parameter1 parameter2
        else
             total=$[ $1 + $2 ]
             echo $1 + $2 is $total
        fi
        exit
    $
    $ ./addem.sh
        Usage: addem.sh parameter1 parameter2
    $ ./addem.sh 17
        Usage: addem.sh parameter1 parameter2
    $ ./addem.sh 17 25
        17 + 25 is 42
使用 !#获取变量,不能使用$#,当命令行中没有任何参数时,$#的值即为0,但${!#}会返回命令行中的脚本名。
    $ cat goodlastparamtest.sh
        #!/bin/bash
        echo The number of parameters is $#
        echo The last parameter is ${!#}
        exit
    $ ./goodlastparamtest.sh one two three four
        The number of parameters is 4
        The last parameter is four
    $ ./goodlastparamtest.sh
        The number of parameters is 0
        The last parameter is ./goodlastparamtest.sh

2.2,获取所有的数据 $* $@

抓取命令行中的所有参数。用两个特殊变量即可.

$* 变量和 $@变量 可以轻松访问所有参数,它们各自包含了所有的命令行参数。

$*变量 将所有的命令行参数 视为一个单词。
$@变量 将所有的命令行参数 视为同一字符串中的多个独立的单词,以便你能遍历并处理全部参数
    $ cat grabbingallparams.sh
        #!/bin/bash
        echo "Using the \$* method: $*"
        echo "Using the \$@ method: $@"
        exit
    $ ./grabbingallparams.sh alpha beta charlie delta
        Using the $* method: alpha beta charlie delta
        Using the $@ method: alpha beta charlie delta
    $ cat grabdisplayallparams.sh
        #!/bin/bash
        echo "Using the \$* method: $*"
        count=1
        for param in "$*"
        do
             echo "\$* Parameter #$count = $param"
             count=$[ $count + 1 ]
        done
        echo "Using the \$@ method: $@"
        count=1
        for param in "$@"
        do
             echo "\$@ Parameter #$count = $param"
             count=$[ $count + 1 ]
        done
        exit
    $ ./grabdisplayallparams.sh alpha beta charlie delta
        Using the $* method: alpha beta charlie delta
        $* Parameter #1 = alpha beta charlie delta
        Using the $@ method: alpha beta charlie delta
        $@ Parameter #1 = alpha
        $@ Parameter #2 = beta
        $@ Parameter #3 = charlie
        $@ Parameter #4 = delta

3,移动参数 shift n

shift命令 可用于操作命令行参数。shift命令会根据 命令行参数 的相对位置进行移动。

使用shift命令时,默认情况下会将每个 位置变量 值都向左移动一个位置.
因此变量$3的值会移入$2,变量$2的值会移入$1,而变量$1的值则会被删除(变量$0的值(脚本名)不会改变).
数被移出,值就被丢弃无法再恢复。!!

在不知道到底有多少参数的时候。你可以只操作第一个位置变量,移动参数,然后继续处理该变量;
    $ cat shiftparams.sh
        #!/bin/bash
        echo
        echo "Using the shift method:"
        count=1
        while [ -n "$1" ]
        do
             echo "Parameter #$count = $1"
             count=$[ $count + 1 ]
             shift
        done
        echo
        exit
    $ ./shiftparams.sh alpha bravo charlie delta
        Using the shift method:
        Parameter #1 = alpha
        Parameter #2 = bravo
        Parameter #3 = charlie
        Parameter #4 = delta
        
可以一次性移动多个位置。提供参数,指明要移动的位置数.

4,处理选项

选项是在连字符之后出现的单个字母,能够改变命令的行为。

4.1,查找选项

可以像处理命令行参数一样处理命令行选项。
4.1.1,处理简单选项
可以使用 shift 处理命令行选项.使用 case判断 是否为参数;
    $ cat extractoptions.sh
        #!/bin/bash
        echo
        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
        echo
        exit
    $ ./extractoptions.sh -a -b -c -d
        Found the -a option
        Found the -b option
        Found the -c option
        -d is not an option
4.1.2,分离参数和选项 –
同时使用选项和参数的情况时,使用特殊字符双连字符(--)将两者分开,字符告诉脚本选项何时结束,普通参数何时开始.

shell 用双连字符表明选项部分结束。在双连字符后,脚本将剩下的部分作为参数处理.
    $ cat extractoptionsparams.sh
        #!/bin/bash
        echo
        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
        echo
        exit
    $ ./extractoptionsparams.sh -a -b -c -- test1 test2 test3
        Found the -a option
        Found the -b option
        Found the -c option
        Parameter #1: test1
        Parameter #2: test2
        Parameter #3: test3
4.1.3,处理含值的选项 shift
有些选项需要一个额外的参数值。类似:
 $ ./testing.sh -a test1 -b -c -d test2

当命令行选项要求额外的参数时,脚本必须能够检测到并正确地加以处理。
    $ cat extractoptionsvalues.sh
        #!/bin/bash
        echo
        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
        exit
    $ ./extractoptionsvalues.sh -a -b BValue -d
        Found the -a option
        Found the -b option with parameter value BValue
        -d is not an option

4.2,getopt命令

合并选项是一种很常见的用法,希望脚本有良好的用户体验,特性不能少.
    $ ./extractoptionsvalues.sh -ac
        -ac is not an option

getopt 命令能够识别命令行参数,简化解析过程。
4.2.1,命令格式 :
getopt命令可以接受一系列任意形式的命令行选项和参数.
格式:
    getopt optstring parameters
optstring 定义了有效的命令行选项字母 和 哪些选项字母需要参数值。

在 optstring 中列出要在脚本中用到的每个命令行选项字母。
在每个需要参数值的选项字母后面加一个冒号。 !!!
getopt命令会基于定义的 optstring 解析提供的参数。

    $ getopt ab:cd -a -b BValue -cd test1 test2
        -a -b BValue -c -d -- test1 test2

optstring 未包含指定的选项,默认情况下,getopt命令会产生一条错误消息, 使用 -q 选项忽略.
    $ getopt ab:cd -a -b BValue -cde test1 test2
        getopt: invalid option -- 'e'
         -a -b BValue -c -d -- test1 test2
    $ getopt -q ab:cd -a -b BValue -cde test1 test2
        -a -b 'BValue' -c -d -- 'test1' 'test2'
4.2.2,在脚本中使用getopt + set
要使用 getopt命令生成的 格式化版本替换已有的命令行选项和参数。需要使用 set命令。

set 命令有一个选项是双连字符(--),可以将 位置变量的值 替换成 set命令所指定的值。

将脚本的命令行参数传给 getopt命令,再将 getopt命令的输出传给set命令,用getopt格式化后的命令行参数来替换原始的命令行参数.
位置变量原先的值会被 getopt命令的输出替换掉。
    set -- $(getopt -q ab:cd "$@")
示例:
    $ cat extractwithgetopt.sh
        #!/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
        exit
    $ ./extractwithgetopt.sh -ac
        Found the -a option
        Found the -c option
    ## getopt命令 不擅长处理带空格和引号的参数值。会将空格当作参数分隔符!!
    $ ./extractwithgetopt.sh -c -d -b BValue -a "test1 test2" test3
        Found the -c option
        -d is not an option
        Found the -b option with parameter value 'BValue'
        Found the -a option
        Parameter #1: 'test1
        Parameter #2: test2'
        Parameter #3: 'test3'
4.2.3,使用getopts命令
getopts相比 getopt 多了一些扩展功能。
getopt在将命令行中选项和参数处理后只生成一个输出,getopts 能够和已有的 shell位置变量配合默契。

getopts每次只处理一个检测到的命令行参数。在处理完所有的参数后,getopts会退出并返回一个大于0的退出状态码。非常适合用在解析命令行参数的循环中。
getopts命令有 可以在参数值中加入空格.
格式:
    getopts optstring variable
有效的选项字母 在 optstring 中列出,
    如果选项字母要求有参数值,就在其后加一个冒号。
    不想显示错误消息的话,可以在 optstring 前加一个冒号 :。
    getopts命令 将当前参数保存在命令行中定义的 variable 中。

getopts 命令要用到两个环境变量。
    OPTARG 环境变量保存 选项需要加带的参数值。
    OPTIND 环境变量保存 getopts正在处理的参数位置。在处理完当前选项之后就能继续处理.
    
while语句定义了getopts命令,指定要查找哪些命令行选项,以及每次迭代时存储它们的变量名(opt)。

示例:
    $ cat extractwithgetopts.sh
        #!/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 parameter value $OPTARG";;
                  c) echo "Found the -c option" ;;
                  *) echo "Unknown option: $opt" ;;
             esac
        done
        exit
    $ ./extractwithgetopts.sh -ab "BValue BValue2" -c
        Found the -a option
        Found the -b option with parameter value BValue BValue2
        Found the -c option

可以将选项字母和参数值写在一起 之间不加空格:
    $ ./extractwithgetopts.sh -abBValue
        Found the -a option
        Found the -b option with parameter value BValue
可以将在命令行中找到的所有未定义的选项统一输出成问号,optstring中未定义的选项字母会以问号形式传给脚本:
    $ ./extractwithgetopts.sh -ade
        Found the -a option
        Unknown option: ?
        Unknown option: ?
getopts 命令知道何时停止处理选项,并将参数留给你处理。
在处理每个选项时,getopts会将OPTIND环境变量值增1。处理完选项后,可以使用 shift命令 和 OPTIND值 来移动参数:
    $ cat extractoptsparamswithgetopts.sh
        #!/bin/bash
        while getopts :ab:cd opt
        do
             case "$opt" in
                  a) echo "Found the -a option" ;;
                  b) echo "Found the -b option with parameter value $OPTARG";;
                  c) echo "Found the -c option" ;;
                  d) echo "Found the -d option" ;;
                  *) echo "Unknown option: $opt" ;;
             esac
        done
        echo $OPTIND
        echo $@
        shift $[ $OPTIND - 1 ]
        echo $@
        echo $OPTIND
        
        count=1
        for param in "$@"
        do
             echo "Parameter $count: $param"
             count=$[ $count + 1 ]
        done
        exit
    $ ./extractoptsparamswithgetopts.sh -db BValue test1 test2
        Found the -d option
        Found the -b option with parameter value Bvalue
        3
        -db Bvalue test1 test2
        test1 test2
        3
        Parameter 1: test1
        Parameter 2: test2

5,选项标准化

在Linux中,有些选项字母在某种程度上已经有了标准含义。在shell脚本中支持这些选项,脚本会更友好。
选项描述
-a显示所有对象
-c生成计数
-d指定目录
-e扩展对象
-f指定读入数据的文件
-h显示命令的帮助信息
-i忽略文本大小写
-l产生长格式输出
-n使用非交互模式(批处理)
-o将所有输出重定向至指定的文件
-q以静默模式运行
-r递归处理目录和文件
-s以静默模式运行
-v生成详细输出
-x排除某个对象
-y对所有问题回答yes

6,获取用户输入 read

尽管命令行选项和参数是从脚本用户处获取输入的一种重要方式,但有时候可能想要在脚本运行时询问用户并等待用户回答。
bash shell提供了read命令。

6.1,基本读取 read -p $REPLY

read命令从标准输入(键盘)或另一个文件描述符中接受输入。获取输入后,read命令 将数据存入变量。
    $ cat askname.sh
        #!/bin/bash
        echo -n "Enter your name: "  ## echo -n选项,不会在字符串末尾输出换行符.
        read name
        echo "Hello $name, welcome to my script."
        exit
    $ ./askname.sh
        Enter your name: Richard Blum
        Hello Richard Blum, welcome to my script.
        
read命令也提供了-p选项,允许直接指定存入的变量提示符:
    $ cat askage.sh
        #!/bin/bash
        read -p "Please enter your age: " age
        days=$[ $age * 365 ]
        echo "That means you are over $days days old!"
        exit
    $ ./askage.sh
        Please enter your age: 30
        That means you are over 10950 days old!

如果指定多个变量,则输入的每个数据值 都会分配给 变量列表中的下一个变量。输入数据多于变量数量,那么剩余的数据就全部分配给最后一个变量.
    $ cat askfirstlastname.sh
        #!/bin/bash
        read -p "Enter your first and last name: " first last
        echo "Checking data for $last, $first..."
        exit
    $ ./askfirstlastname.sh
        Enter your first and last name: Richard Blum
        Checking data for Blum, Richard...
可以 在read命令中不指定任何变量,此时 read命令便会将接收到的所有数据都放进 特殊环境变量 REPLY 中(也仅在不指定变量时才放入 REPLY):
REPLY环境变量包含输入的所有数据
    $ cat asknamereply.sh
        #!/bin/bash
        read -p "Enter your name: "
        echo
        echo "Hello $REPLY, welcome to my script."
        exit
    $ ./asknamereply.sh
        Enter your name: Christine Bresnahan
        Hello Christine Bresnahan, welcome to my script.

6.2,超时 read -t

使用 read命令时,脚本可能会一直苦等着用户输入。
如果不管是否有数据输入,脚本都必须继续执行,可以用-t选项来指定计时器。

-t选项会指定 read命令 等待输入的 秒数。如果计时器超时,则read命令会返回非0退出状态码.
    $ cat asknametimed.sh
        #!/bin/bash
        if read -t 5 -p "Enter your name: " name
        then
             echo "Hello $name, welcome to my script."
        else
             echo "Sorry, no longer waiting for name."
        fi
        exit
    $ ./asknametimed.sh
        Enter your name: Christine
        Hello Christine, welcome to my script.
    $ ./asknametimed.sh
        Enter your name:
        Sorry, no longer waiting for name.

可以不对输入过程计时,而是统计输入的字符数。当字符数达到预设值时,就自动退出,将已输入的数据赋给变量:
-n选项和数值,告诉read命令在接收到x个字符后退出。
    $ cat continueornot.sh
        #!/bin/bash
        read -n 1 -p "Do you want to continue [Y/N]? " answer
        case $answer in
        Y | y) echo
               echo "Okay. Continue on...";;
        N | n) echo
               echo "Okay. Goodbye"
               exit;;
        esac
        echo "This is the end of the script."
        exit
    $ ./continueornot.sh
        Do you want to continue [Y/N]? Y
        Okay. Continue on...
        This is the end of the script.
    $ ./continueornot.sh
        Do you want to continue [Y/N]? n
        Okay. Goodbye

6.3,无显示读取 read -s

需要从脚本用户处得到输入,而不在屏幕上显示输入信息
s选项 可以避免在 read命令中输入的数据出现在屏幕上(其实数据还是会被显示,只不过read命令将文本颜色设成了跟背景色一样)
    $ cat askpassword.sh
        #!/bin/bash
        read -s -p "Enter your password: " pass
        echo
        echo "Your password is $pass"
        exit
    $ ./askpassword.sh
        Enter your password:
        Your password is Day31Bright-Test

6.4,从文件中读取 cat File | while read var

可以使用read命令读取文件。每次调用read命令都会从指定文件中读取一行文本。
当文件中没有内容可读时,read命令会退出并返回 非0退出状态码。

将文件数据传给 read命令。最常见的方法是对文件使用cat命令,将结果通过管道直接传给含有read命令的while命令
    $ cat readfile.sh
        #!/bin/bash
        count=1
        cat $HOME/scripts/test.txt | while read line
        do
             echo "Line $count: $line"
             count=$[ $count + 1 ]
        done
        echo "Finished processing the file."
        exit
    $ cat $HOME/scripts/test.txt
        The quick brown dog jumps over the lazy fox.
        This is a test. This is only a test.
        O Romeo, Romeo! Wherefore art thou Romeo?
    $ ./readfile.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.

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值