Skip to content

Latest commit

 

History

History
403 lines (343 loc) · 11.6 KB

File metadata and controls

403 lines (343 loc) · 11.6 KB

处理用户输入

shell命令后面可以跟参数,这些参数被称之为位置参数。位置参数变量是标准的数字:$0是程序名,$1是第一个参数,$2是第二个参数,依次类推,直到第九个参数$9。就要用花括号括起来,例如${10}这样子。

例子:
#!/bin/bash
total=$[ $1 * $2 ]
echo The first is $1
echo The second is $2
echo The total value is $total
echo $0
输出:
[root@vultr /]# ./test2 10 20 
The first is 10
The second is 20
The total value is 200
./test2

参数也可以是字符串,但是字符串之间有空格,要用引号(单双引号都可以)括起来,不然会导致错误。

直接$0会把脚本的路劲也给带上,不想要路径的话可以用basename,用这个同时能进行一些其他操作。

name=$(basename $0)
if [ $name = "addem" ] 
then    
total=$[ $1 + $2 ] 
elif [ $name = "multem" ] 
then    
total=$[ $1 * $2 ] 
fi 
echo 
echo The calculated value is $total

[root@vultr /]# cp test2 addem
[root@vultr /]# cp test2 multem

[root@vultr /]# ./addem 1 2

The calculated value is 3
[root@vultr /]# ./multem 1 2

The calculated value is 2

有时候,我们使用脚本并不会传入脚本,这有可能导致脚本报错,这是不希望发生的事情,所以需要在脚本当中进行检测。

if [ -n "$1" ] #字符串检测是否长度为0
then
    echo Hello $1, glad to meet you
else
    echo "Sorry, you did not identify yourself."
fi

[root@vultr home]# ./test1.sh test
Hello test, glad to meet you

假如我们想知道一共传了多少参数进来怎么办,这就用到了特殊变量$#

#!/bin/bash
echo There were $# parameters supplied。

[root@vultr home]# ./test1.sh test
There were 1 parameters supplied。

是不是想到知道多少个个数了,那么能不能直接拿这个变量取最后一个参数呢?答案是不能。直接这样使用会报错,只能将他进行改变一下把美元符号变成叹号。!#

params=$#
echo
echo The last parameter is $params
echo The last parameter is ${!#}
echo

[root@vultr home]# ./test1.sh 1 2 3 4 5 6 7

The last parameter is 7
The last parameter is 7

这个测试将$#变量的值赋给了变量params,然后也按特殊命令行参数变量的格式 使用了该变量。两种方法都没问题。重要的是要注意,当命令行上没有任何参数时,$#的值为0, params变量的值也一样,但${!#}变量会返回命令行用到的脚本名。

我们想取所有的参数,但是像用$#取参数个数,然后一个个遍历太麻烦了。我们可以用$*或者$@

#!/bin/bash
echo
count=1
for param in "$*"
do
    echo "\$* Parameter #$count = $param"
    count=$[ $count + 1 ]
done
#
echo
count=1
for param in "$@"
do
    echo "\$@ Parameter #$count = $param"
    count=$[ $count + 1 ]
done

[root@vultr home]# ./test1.sh rich barbara katie jessica

$* Parameter #1 = rich barbara katie jessica

$@ Parameter #1 = rich
$@ Parameter #2 = barbara
$@ Parameter #3 = katie
$@ Parameter #4 = jessica

$*变量会将所有参数当成单个参数,而$@变量会单独处理每个参数。这是遍历命令行参数的一个绝妙方法。

shell脚本有一个移动变量的命令shift,在使用shift命令时,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3 的值会移到$2中,变量$2的值会移到$1中,而变量$1的值则会被删除(注意,变量$0的值,也 就是程序名,不会改变)。

echo
count=1
while [ -n $"$1" ]
do
    echo "Parameter #$count = $1"
    count=$[ $count + 1 ]
    shift
done

[root@vultr home]# ./test1.sh rich barbara katie jessica 

Parameter #1 = rich
Parameter #2 = barbara
Parameter #3 = katie
Parameter #4 = jessica

使用shift命令的时候要小心。如果某个参数被移出,它的值就被丢弃了,无法再恢复。也可以给shift传递参数,比如想移动两位,可以shift 2

像是破折号加字符其实也是传入的参数,我们可以用case来简单的进行选择。

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

[root@vultr home]# ./test1.sh -a -b -c -d

Found the -a option
Found the -b option
Found the -c option
-d is not an option

这只是最简单的选择,我们当然不可能就这么简单了,我们不止要传入选项,还要传入参数。所以我们需要用一个特殊符号来将二者分开,linux当中可以用双破折号(--)

#!/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

./test1.sh -a -b -c -d -- 1 2 3 

Found the -a option
Found the -b option
Found the -c option
-d is not an option
Parameter #1: 1
Parameter #2: 2
Parameter #3: 3

想要选项后面跟参数

#!/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 $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

[root@vultr home]# ./test1.sh -a -b haha -c -d -- 1 2 3 

Found the -a option
Found the -b option, with parameter haha
Found the -c option
-d is not an option
Parameter #1: 1
Parameter #2: 2
Parameter #3: 3

但是我们平时会用选项组合,按照上面的写法,好像是实现不了,想要实现,每种可能性要都写一个case。这也太麻烦了啊!

所以我们可以使用getopt
格式:getopt optstring parameters
首先,在optstring中列出你要在脚本中用到的每个命令行选项字母。然后,在每个需要参 数值的选项字母后加一个冒号。getopt命令会基于你定义的optstring解析提供的参数。
例子:

getopt ab:cd -a -b test1 -cd test2 test3
  -a -b test1 -c -d -- test2 test3 

[root@vultr home]# getopt -q ab:cd -a -b test1 -cde test2 test3
 -a -b 'test1' -c -d -- 'test2' 'test3'

这里的-q是禁止报错,我们并没有设定e这个选项,一般情况下是会报出错误的,不过用了这个选项之后,就不会报错。

可以在脚本中使用getopt来格式化脚本所携带的任何命令行选项或参数,但用起来略微复杂。
方法是用getopt命令生成的格式化后的版本来替换已有的命令行选项和参数。用set命令能 够做到。 在第6章中,你就已经见过set命令了。set命令能够处理shell中的各种变量。 set命令的选项之一是双破折线(--),它会将命令行参数替换成set命令的命令行值。 然后,该方法会将原始脚本的命令行参数传给getopt命令,之后再将getopt命令的输出传 给set命令,用getopt格式化后的命令行参数来替换原始的命令行参数,看起来如下所示。
set -- $(getopt -q ab:cd "$@") 现在原始的命令行参数变量的值会被getopt命令的输出替换,而getopt已经为我们格式化 好了命令行参数。 利用该方法,现在就可以写出能帮我们处理命令行参数的脚本。

这部分其实没搞懂,先记下来。

#!/bin/bash
set -- $(getopt -q ab:cd "$@")
echo
while [ -n "$1" ]
do
    case "$1" in
        -a) echo "Found the -a option" ;;
        -b) param="$2"
            echo "Found the -b option, with parameter $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

[root@vultr home]# ./test1.sh -ab test1 -cde

Found the -a option
Found the -b option, with parameter 'test1'
Found the -c option
-d is not an option

getopt命令并不擅长处理带空格和引号的参数值。它会将空格当作参数分隔符,而不是根 据双引号将二者当作一个参数。

可以使用getopts

getopts optstring variable 

optstring值类似于getopt命令中的那个。有效的选项字母都会列在optstring中,如果 选项字母要求有个参数值,就加一个冒号。要去掉错误消息的话,可以在optstring之前加一个冒号。(getopt 是用的-q)

#!/bin/bash
echo
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" ;;
        *) echo "Unknown option: $opt" ;;
    esac
done 
#
shift $[ $OPTIND - 1 ]
#
echo 
count=1
for param in "$@"
do
    echo "Parameter $count: $param"
    count=$[ $count + 1 ]
done

[root@vultr home]# ./test1.sh -ab test1 -cde test2 test3 test4

Found the -a option
Found the -b option, with value test1
Found the -c option
Unknown option: d
Unknown option: ?

Parameter 1: test2
Parameter 2: test3
Parameter 3: test4

getopts命令会用到两个环境变量。如果选项需要跟一个参数值,OPTARG环境变量就会保 存这个值。OPTIND环境变量保存了参数列表中getopts正在处理的参数位置。

在getopts处理每个选项时, 它会将OPTIND环境变量值增一。在getopts完成处理时,你可以使用shift命令和OPTIND值来 移动参数。

常用的linux命令选项

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

与用户输入之间的交互用到read

#!/bin/bash
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my program."

[root@vultr home]# ./test1.sh
Enter your name: hhh 
Hello hhh, welcome to my program.

当然我们也可以简化用read的-p选项,然后在后面有几个变量,我们就能记住几个输入参数。

#!/bin/bash
read -p "Please enter your name:" first last
echo "Checking data for $last, $first…"

[root@vultr home]# ./test1.sh
Please enter your name:hhh lll
Checking data for lll, hhh…

假如没有设定变量,所有的输入参数都会被记住在特殊环境变量REPLY

这种交互是没有输入他就一直等待输入,这明显也不是合理的。所以可以用read的-t选项。

read -t 5 -p "Please enter your name: " name

我们也可以限定输入字符数量。

#!/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"

[root@vultr home]# ./test1.sh
Do you want to continue [Y/N]?y
fine, continue on...
This is the end of the script

-s选项可以避免在read命令中输入的数据出现在显示器上(实际上,数据会被显示,只是 read命令会将文本颜色设成跟背景色一样。