首页 > 代码库 > Linux Shell脚本编写——使用结构化命令(四)
Linux Shell脚本编写——使用结构化命令(四)
命令行参数
向shell脚本传数据的最基本方法是使用命令行参数,命令行参数允许在运行脚本时向命令行添加数据值
读取参数
bash shell会将一些称为位置参数的特殊变量分配给命令行输入的所有参数,甚至包括shell执行的程序的名字,$0是程序名,$1是第一个参数,$2是第二个参数
代码4-1
root@lejian:/data# cat demo1 #!/bin/bash factorial=1 for ((i=1;i<=$1;i++)) do factorial=$[ $factorial * $i ] done echo "The factorial of $1 is $factorial" root@lejian:/data# ./demo1 5 The factorial of 5 is 120
输入更多的命令项用空格分开
代码4-2
root@lejian:/data# cat demo2 #!/bin/bash sum=$[ $1 + $2 ] echo "The first parameter is $1" echo "The second parameter is $2" echo "The sum is $sum" root@lejian:/data# ./demo2 3 5 The first parameter is 3 The second parameter is 5 The sum is 8
当输入为字符串时如果存在空格,必须使用单引号或双引号
代码4-3
root@lejian:/data# cat demo3 #!/bin/bash echo "Hello $1,glad to meet you" echo "Hello $2,glad to meet you" root@lejian:/data# ./demo3 John "Rich blum" Hello John,glad to meet you Hello Rich blum,glad to meet you
如果脚本需要多于9哥命令行参数,需要在数字周围加上花括号如:${10}、${11}
代码4-4
root@lejian:/data# cat demo4 #!/bin/bash sum=$[ $10 + $11 ] echo "The tenth paramter is ${10}" echo "The eleventh paramter is ${11}" echo "The sum is $sum" root@lejian:/data# ./demo4 1 2 3 4 5 6 7 8 9 10 11 The tenth paramter is 10 The eleventh paramter is 11 The sum is 21
当传给$0变量的真实字符串是整个脚本的路径时,程序中就会使用整个路径,而不仅仅是程序名
代码4-5
root@lejian:/data# cat demo5 #!/bin/bash echo "The command entered is : $0" root@lejian:/data# /data/demo5 The command entered is : /data/demo5 root@lejian:/data# ./demo5 The command entered is : ./demo5
basename命令只返回程序名而不包括路径
代码4-6
root@lejian:/data# cat addem #!/bin/bash name=`basename $0` if [ $name = "addem" ] then total=$[ $1 + $2 ] elif [ $name = "multem" ] then total=$[ $1 * $2 ] fi echo "The total is $total" root@lejian:/data# ./addem 2 5 The total is 7 root@lejian:/data# cp -s addem multem root@lejian:/data# ./multem 2 5 The total is 10
可以使用-n检查命令行参数中是否有数据,如果要对命令行参数进行字符串比较最好加上双引号
代码4-7
root@lejian:/data# cat demo6 #!/bin/bash if [ -n "$1" ] then echo "Hello,$1" else echo "Sorry, you did not identify yourself" fi root@lejian:/data# ./demo6 Tom Hello,Tom root@lejian:/data# ./demo6 Sorry, you did not identify yourself
$#特殊变量含有脚本运行时就有的命令行参数的个数
代码4-8
root@lejian:/data# cat demo1 #!/bin/bash echo "There were $# parameters supplied" root@lejian:/data# ./demo1 1 2 3 4 5 There were 5 parameters supplied root@lejian:/data# ./demo1 Tom "Hello World" There were 2 parameters supplied
代码4-9
root@lejian:/data# cat demo2 #!/bin/bash if [ $# != 2 ] then echo "Usage:demo2 a b" else sum=$[ $1 + $2 ] echo "The sum is $sum" fi root@lejian:/data# ./demo2 Usage:demo2 a b root@lejian:/data# ./demo2 3 Usage:demo2 a b root@lejian:/data# ./demo2 3 5 The sum is 8 root@lejian:/data# ./demo2 3 5 8 Usage:demo2 a b
注:$#虽然代表命令行参数的个数,但${$#}并不能显示最后一个命令行参数的变量
使用${!#}可以显示最后一个参数,或者将$#赋给一个变量param,然后也按特殊命令行参数变量的格式使用了该变量。但要注意的是,当命令行上没有任何参数时,$#的值为0,但${!#}变量会返回命令行用到的脚本名
代码4-10
root@lejian:/data# cat demo3 #!/bin/bash echo "The last parameter was $#" echo "The last parameter was ${!#}" root@lejian:/data# ./demo3 The last parameter was 0 The last parameter was ./demo3 root@lejian:/data# ./demo3 1 2 3 4 5 6 The last parameter was 6 The last parameter was 6
$*和$@变量提供了对所有参数的快速访问,$*变量会将命令行上提供的所有参数当做单个单词保存,$@变量会将命令行上提供的所有参
数当做同一个字符串中多个独立的单词
代码4-11
root@lejian:/data# cat demo4 #!/bin/bash count=1 for param in "$*" do echo "\$* Parameter #$count = $param" ((count++)) done count=1 echo "——————————————————————" for param in "$@" do echo "\$@ Parameter #$count = $param" ((count++)) done root@lejian:/data# ./demo4 Hadoop Spring MyBatis Hibernate Shiro $* Parameter #1 = Hadoop Spring MyBatis Hibernate Shiro —————————————————————— $@ Parameter #1 = Hadoop $@ Parameter #2 = Spring $@ Parameter #3 = MyBatis $@ Parameter #4 = Hibernate $@ Parameter #5 = Shiro
shift命令默认情况下它会将每个参数变量的索引值减1,所以,变量$3的值会移到$2,变量$2的值会移到$1,而变量$1的值则会被删除
(变量$0的值,也就是程序名不会变),每执行一次shift命令,所有参数的位置移动一位
代码4-12
root@lejian:/data# cat demo5 #!/bin/bash count=1 while [ -n "$1" ] do echo "Parameter #$count = $1" ((count++)) shift done root@lejian:/data# ./demo5 Hadoop Spring MyBatis Hibernate Shiro Parameter #1 = Hadoop Parameter #2 = Spring Parameter #3 = MyBatis Parameter #4 = Hibernate Parameter #5 = Shiro
也可以给shift命令提供一个参数来执行多位移动
代码4-13
root@lejian:/data# cat demo6 #!/bin/bash echo "The original parameters : $*" shift 2 echo "Here‘s the new first parameter : $1" root@lejian:/data# ./demo6 1 2 3 4 5 6 The original parameters : 1 2 3 4 5 6 Here‘s the new first parameter : 3
处理选项,选项是跟在单破折线后面的单个字母,能改变命令的行为
shell用特殊字符(--)双破折线来表明选项结束了,将剩余命令行参数交给后续程序处理
代码4-14
root@lejian:/data# cat demo1 #!/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++)) done root@lejian:/data# ./demo1 -a -b -c test1 test2 test3 Found the -a option Found the -b option Found the -c option test1 is not an option test2 is not an option test3 is not an option root@lejian:/data# ./demo1 -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
getopt命令可以接受一系列任意形式的命令行选项和参数,并自动将他们转换成适当的格式
getopt optstring options parameters
optstring定义了命令行有效的选项字母,每个需要参数值的选项字母后加一个冒号
代码4-15
root@lejian:/data# getopt ab:cd -ab test1 -cd test2 test3 -a -b test1 -c -d -- test2 test3
如代码4-15所示,optstring定义了4哥有效选项字母,a、b、c和、d。它还定义了选项字母b需要一个参数值,当getopt 命令运行时,它会检查提供的参数列表,并基于optstring解析。它会自动将-cd选项分成两个单独的选项,并插入双破折线分开航中的额外参数
如代码4-16,如果置顶一个不在optstring中的选项,默认情况下,getopt 命令会产生一条错误的信息,加上-q则可以忽略这条信息:
代码4-16
root@lejian:/data# getopt ab:cd -ab test1 -cde test2 test3 getopt: invalid option -- ‘e‘ -a -b test1 -c -d -- test2 test3 root@lejian:/data# getopt -q ab:cd -ab test1 -cde test2 test3 -a -b ‘test1‘ -c -d -- ‘test2‘ ‘test3‘
set命令的选项之一是双破折线,该方法会将原始的脚本的命令行参数传给getopt命令,之后再将getopt的输出传给set命令,set会将命令行参数值替换成传递给它的值
代码4-17
root@lejian:/data# cat demo2 #!/bin/bash set -- `getopt -q ab:c "$@"` while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option";; -b) param="$2" echo "Found -b option. with parameter value $param" shift;; -c) echo "Found the -c option";; --) shift break;; *) echo "$1 is not a option" esac shift done count=1 for param in "$@" do echo "Parameter #$count : $param" ((count++)) done root@lejian:/data# ./demo2 -ac Found the -a option Found the -c option root@lejian:/data# ./demo2 -ab test1 -cd "test2 test3" test4 test5 Found the -a option Found -b option. with parameter value ‘test1‘ Found the -c option Parameter #1 : ‘test2 Parameter #2 : test3‘ Parameter #3 : ‘test4‘ Parameter #4 : ‘test5‘
从代码4-17可以看出,getopt命令并不擅长处理带空格的参数值,它会将空格当作参数分隔符,而不是根据双引号将二者当作一个参数,幸而,可以使用getopts解决这一问题
getopts在每次调用它时,只处理一个命令行上检测到的参数。处理完所有参数后,它会退出并返回一个大于0的退出状态码
getopts optstring variable
getopts 中的optstring 与之前类似,如果选项字母要求有参数值的话,就加一个冒号,如果要去掉错误信息的话,就在optstring 之前加一个冒号
getopts 命令会用到两个环境变量。如果选项需要跟一个参数值,OPTARG环境变量就会保存这个值。OPTIND环境变量保存了参数列表中getopts 正在处理的参数位置
代码4-18
root@lejian:/data# cat demo3 #!/bin/bash while getopts :ab:c opt do case "$opt" in a) echo "Found the -a option";; b) echo "Fount the -b option, with value $OPTARG";; c) echo "Found the -c option";; *) echo "Unknow option : $opt ";; esac done root@lejian:/data# ./demo3 -ab test1 -c Found the -a option Fount the -b option, with value test1 Found the -c option root@lejian:/data# ./demo3 -b "test1 test2" -a Fount the -b option, with value test1 test2 Found the -a option root@lejian:/data# ./demo3 -d Unknow option : ? root@lejian:/data# ./demo3 -acde Found the -a option Found the -c option Unknow option : ? Unknow option : ? root@lejian:/data# ./demo3 -abtest1 Found the -a option Fount the -b option, with value test1
如代码4-18,getopts可以很好的处理包含空格的参数值,能够将命令行上找到的所有未定义的选项统一输出成问号,optstring 中未定义的选项字母会以问号形式发送给代码,同时可以将选项字母和参数值放一起使用而不用加空格
OPTIND和shift命令一起使用来移动参数
代码4-19
root@lejian:/data# cat demo4 #!/bin/bash while getopts :ab:cd opt do case "$opt" in a) echo "Found the -a option";; b) echo "Fount the -b option, with value $OPTARG";; c) echo "Found the -c option";; d) echo "Found the -d option";; *) echo "Unknow option : $opt ";; esac done shift $[ $OPTIND - 1 ] count=1 for param in "$@" do echo "Parameter $count : $param" ((count++)) done root@lejian:/data# ./demo4 -ab test1 -d test2 test3 test4 Found the -a option Fount the -b option, with value test1 Found the -d option Parameter 1 : test2 Parameter 2 : test3 Parameter 3 : test
获取用户输入
read命令接受从标准输入后,会将数据放进一个标准变量
代码4-20
root@lejian:/data# cat demo5 #!/bin/bash echo -n "Enter your name:" read name echo "Hello $name, welcome to my program" root@lejian:/data# ./demo5 Enter your name:Tom Hello Tom, welcome to my program
read命令包含-p选项,允许在字符串后接收参数 ,参数与字符串必须用空格分隔
代码4-21
root@lejian:/data# cat demo1 #!/bin/bash read -p "Enter your name:" first last echo "Your name is $last. $first..." root@lejian:/data# ./demo1 Enter your name:Rich Blum Your name is Blum. Rich...
读取多个输入
代码4-22
root@lejian:/data# cat demo6 #!/bin/bash read -p "Please enter your age:" age days=$[ $age * 365 ] echo "That makes you over $days daysold" root@lejian:/data# ./demo6 Please enter your age:20 That makes you over 7300 daysold
如果read命令行中不指定变量,会将接收到的数据都存放进特殊环境变量REPLY中
代码4-23
root@lejian:/data# cat demo2 #!/bin/bash read -p "Enter a number:" factorial=1 for ((i=1;i<=$REPLY;i++)) do factorial=$[ $factorial * $i ] done echo "The factorial of $REPLY is $factorial" root@lejian:/data# ./demo2 Enter a number:5 The factorial of 5 is 120
代码4-24
root@lejian:/data# cat demo3 #!/bin/bash read -p "Enter your parameter:" echo "Your parameter:$REPLY" root@lejian:/data# ./demo3 Enter your parameter:6 Your parameter:6 root@lejian:/data# ./demo3 Enter your parameter:8 9 Your parameter:8 9
read命令如果没有输入就会一直等下下去,可加-t选项来指定一个计时器,当计时器过后,read会返回一个非0状态码
代码4-25
root@lejian:/data# cat demo4 #!/bin/bash echo "time:`date "+%Y-%m-%d %H:%M:%S"`" if read -t 5 -p "Please enter your name:" name then echo "Hello $name, welcome to my script time:`date "+%Y-%m-%d %H:%M:%S"` " else echo "Sorry, too slow! time:`date "+%Y-%m-%d %H:%M:%S"`" fi root@lejian:/data# ./demo4 time:2016-12-04 16:06:06 Please enter your name:Tom Hello Tom, welcome to my script time:2016-12-04 16:06:08 root@lejian:/data# ./demo4 time:2016-12-04 16:06:12 Please enter your name:Sorry, too slow! time:2016-12-04 16:06:17
可以用read命令来对输入的字符计数,当输入的字符达到预设的字符数时,它会自动将输入的数据赋给变量并开始执行往下的程序,将-n
和1一起使用,告诉read命令在接受单个字符后退出
代码2-25
root@lejian:/data# cat demo5 #!/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@lejian:/data# ./demo5 Do you want to continue [Y/N]y Fine,continue on... This is the end of the script
隐藏方式读取,-s选项会阻止将传给read命令的数据显示在显示器上(实际数据会显示,只是read命令会将文本颜色设成跟背景色一样)
代码2-26
root@lejian:/data# cat demo6 #!/bin/bash read -s -p "Enter your password:" pass echo echo "Is your password really $pass" root@lejian:/data# ./demo6 Enter your password: Is your password really hello
将cat的结果通过管道输出给read
代码2-17
root@lejian:/data# cat text Spring MyBatis Hibernate Shiro Hadoop Spark root@lejian:/data# cat demo1 #!/bin/bash count=1 cat text | while read line do echo "Line $count:$line" ((count++)) done root@lejian:/data# ./demo1 Line 1:Spring Line 2:MyBatis Line 3:Hibernate Line 4:Shiro Line 5:Hadoop Line 6:Spark
Linux Shell脚本编写——使用结构化命令(四)