首页 > 代码库 > 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脚本编写——使用结构化命令(四)