1. bash shell脚本中的位置变量

1. 有些脚本运行时是需要输入参数的,在shell脚本中加入指定的位置变量,脚本会在运行时读取位置变量对应的参数,位置变量表示方法如下:

(1)$0:表示脚本自身

(2)$(basename $0):当我们使用位置变量$0时,表示将脚本自身名称作为变量值传递给$0,但是它会传递完整的脚本路径,当我们仅需要脚本的基名的时候,可以使用$(basename $0)来实现

(3)$1-$9:表示脚本后跟的参数的位置,$1表示第一个参数,$2表示第二个参数以此类推;

(4)${#}:#表示大于9的数字,当位置大于9时需使用${}的方式来表示位置

(5)${!#}:表示脚本最后一个参数作为变量传递给${!#}

(6)$#:表示参数个数统计,$#变量会计算脚本所跟参数的个数

(7)$*:表示脚本后跟随的所有参数,但是它会把这些参数当做一个整体传递给$*变量

(8)$@:与$*类似,同样是将脚本后所跟随的所有参数传递给$@变量,但是会把每个变量做为单个个体依次传递给$@。

(9)含空格的参数表示方法:位置变量取值时默认以空格作为分割,当参数包含空格时,需使用""将该参数引起来,这样就可以当成一个独立的参数传递给脚本

(10)shift:脚本参数位移,当在脚本中使用shift时表示将参数的位置向左移动一位

(11)shift n:n用数字表示,shift n表示将参数将左移动n位


2. bash shell脚本位置变量使用示例

2.1 $0

[root@localhost test]# vi test1.sh
#!/bin/bash
#Testing the $0 parameter
#
echo "The zero parameter" is set to: $0
                                                                             
~                                                                               
"test1.sh" [New] 5L, 81C written
[root@localhost test]# chmod u+x test1.sh
[root@localhost test]# ./test1.sh
The zero parameter is set to: ./test1.sh

根据上述脚本可看出$0的取值为脚本名 ./test1.sh


2.2 $(basename $0)

[root@localhost test]# vi test2.sh
#!/bin/bash
#Testing the $(basename $0) parameter
#
echo "The zero parameter" is set to: $(basename $0)

~                                                                                                                                                              
"test2.sh" 5L, 105C written
[root@localhost test]# ./test2.sh
The zero parameter is set to: test2.sh

根据上述示例可看出使用$(basename $0)后,取得的脚本名仅为脚本名本身,不包含路径


2.3 $1-$9

[root@localhost test]# vi test3.sh
#!/bin/bash
#USing one command line parameter
#
factorial=1
for (( number=1; number <= $1; number++ ))
do
        factorial=$[ $factorial * $number ]
done
        echo The factorial of $1 is $factorial
~                                                                                                                                                             
~                                                                               
"test3.sh" 9L, 188C written
[root@localhost test]# ./test3.sh 4
The factorial of 4 is 24

根据上述脚本可看出脚本参数4被赋予给了位置变量$1

[root@localhost test]# vi test4.sh
#!/bin/bash
#Testing tow command lin parameters
#
if [ $# -ge 3 ]
then
        echo "Please input only tow parameters"
elif [ $# -eq 2 ]
then
        if [ $1 -gt $2 ]
        then
                echo "$1 is greater than $2"
        elif [ $1 -eq $2 ]
        then
                echo "$1 is equal to $2"
        else
                echo "$1 is less than $2"
        fi
else
        echo "The character you entered is invalid, please enter the number"
fi
~                                                                               
~                                                                               
~                                                                               
~                                                                               
"test4.sh" 20L, 359C written
[root@localhost test]# ./test4.sh 4 5
4 is less than 5

通过上述脚本可以看出$1和$2分别对应位置1和位置2的参数。


2.4 ${#}

当参数个数大于9时,位置变量用${#}表示,#表示大于9的数字

[root@localhost test]# vi test5.sh
#!/bin/bash
#handing lots of parameters
#
total=$[ ${10} * ${11} ]
echo The tenth parameter is ${10}
echo The eleventh parameter is ${11}
echo The total is $total
~                                                                               
~                                                                                                                                                             
"test5.sh" 7L, 163C written
[root@localhost test]# ./test5.sh 1 2 3 4 5 6 7 8 9 10 11
The tenth parameter is 10
The eleventh parameter is 11
The total is 110


2.5 $#   ${!#}

$#表示参数的个数,${!#}表示最后一个参数

[root@localhost test]# vi test6.sh
#!/bin/bash
#Grabbing the last parameter
#
params=$#
echo The total number of parameters is $#
echo The last parameter is ${!#}
~                                                                               
~                                                                                                                                                             
"test6.sh" 6L, 128C written
[root@localhost test]# ./test6.sh 1 2 4 6 8 9 10 4
The total number of parameters is 8
The last parameter is 4

根据上述脚本和执行结果和看出$#最后显示的是参数的总个数,为8个,${!#}显示的是最后一个参数,为4。${!#}还有一个特殊特性,当脚本后没有参数时将返回脚本名。


2.6 $*  $@

$*和$@都表示所有参数,但是$*表示将所有参数当做一个整体传递给$*,而$@则表示将每个参数按顺序一个个传递给$@

[root@localhost test]# vi test7.sh
#!/bin/bash
#Testing $* and $@
#
count=1
#
for params in "$*"
do
        echo "\$* Paramter #$count = $params"
        count=$[ $count + 1 ]
done
#
#
count=1
for params in "$@"
do
        echo "\$@ Parameter #$conut = $params"
        count=$[ $count + 1 ]
done
~                                                                               
~                                                                                                                                                              
~                                                                               
"test7.sh" 18L, 234C written
[root@localhost test]# ./test7.sh rich barbara katie jessica
$* Paramter #1 = rich barbara katie jessica
$@ Parameter # = rich
$@ Parameter # = barbara
$@ Parameter # = katie
$@ Parameter # = jessica

根据上述脚本可以看到我们分别为$*和$#设置了for循环,从队列$*读取的变量值是将参数作为一个整体,而从$@中是逐个参数船体给$@


2.7 shift n

在shell脚本中shift的作用是将指定位置的参数向左移动,n为移动的位置数,如果没有设置n,则默认为1

[root@localhost test]# vi test8.sh
#!/bin/bash
#Testing shift
#
count=1
for params in "$@"
do
        echo "#$count = $1"
        count=$[ $count + 1 ]
        shift
done
~                                                                               
~                                                                                                                                                              
"test8.sh" 10L, 115C written
[root@localhost test]# ./test8.sh rich barbara katie jessica
#1 = rich
#2 = barbara
#3 = katie
#4 = jessica

根据上述脚本可看出每一执行完一轮循环shift都会将参数左移一位,$1位置的参数在位移后会丢失。$1会读取原$1位置的后一位参数。


[root@localhost test]# vi test9.sh
#!/bin/bash
#Demonstrating a multi-position shift
#
echo "The original parameter: $*"
shift 2 
echo "Here's the new first parameter: $1"
~                                                                                                                                                             
~                                                                               
"test9.sh" [New] 6L, 136C written
[root@localhost test]# chmod u+x test9.sh
[root@localhost test]# ./test9.sh 1 2 3 4 5
The original parameter: 1 2 3 4 5
Here's the new first parameter: 3

根据上述脚本可看出当使用shift 2 后,参数向左移动了2位,所有移动后的$1为3


3. 处理命令行选项

3.1 处理简单选项

可以使用while循环检查选项,使用case命令按照条件输出选项所对应的操作。

[root@localhost test]# vi test1.sh
#!/bin/bash
#Testing simple parameter processing
#
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 "The $1 is not an option" ;;
        esac
        shift
done
~                                                                                                 
~                                                                                                                                                                                                 
~                                                                                                 
[root@localhost test]# ./test1.sh -a -b -c -d
Found the -a option
Found the -b option
Found the -c option
The -d is not an option


3.2 分离选项和参数

可使用使用 -- 号来分离选项和参数当遇到 -- 时终止while循环,同时把 -- 使用shift命令移除

[root@localhost test]# vi test2.sh
#!/bin/bash
#Testing simple parameter processing
#
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 "The $1 is not an option" ;;
        esac
        shift
done
#
count=1
for parameter in "$@"
do
        echo "The #$parameter is a parameter"
        count=$[ $count + 1 ]
done
~                                                                                                                                                                                                  
~                                                                                                 
"test2.sh" 22L, 381C written
[root@localhost test]# ./test2.sh -c -b test1 test2 test3
Found the -c option
Found the -b option
The test1 is not an option
The test2 is not an option
The test3 is not an option
[root@localhost test]# ./test2.sh -c -a -- test1 test2 test3
Found the -c option
Found the -a option
The #test1 is a parameter
The #test2 is a parameter
The #test3 is a parameter


3.3 处理带参数的选项

[root@localhost test]# vi test3.sh
#!/bin/bash
# extracting command line options and values
#
while [ -n "$1" ]
do
        case "$1" in
                -a) echo "Found the -a options";;
                -b) parameter="$2"
                    echo "Found the -b options , with parameter values $parameter"
                    shift;;
                -c) echo "Found the -c options";;
                --) shift
                    break;;
                *) echo "$1 is not options";;
        esac
        shift
done
#
count=1
for parameter in "$@"
do
        echo #$parameter is a parameter
        count=$[ $count + 1 ]
done
~                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
~                                                                                                                                                                                                                                           
"test3.sh" 24L, 442C written
[root@localhost test]# ./test3.sh -a -b test1 -d
Found the -a options
Found the -b options , with parameter values test1
-d is not options


3.4 getopt命令

getopt命令可以接受任何选项和参数,并将他们转换成相应的格式

格式:getopt OPTSRING parameter

带参数的选项后跟:

示例:

[root@localhost test]# getopt ab:cd -a -b test1 -cd test2 test3 test4
 -a -b test1 -c -d -- test2 test3 test4

要在脚本中使用getopt命令,需要使用set命令

示例:(-q 命令表示忽略错误提示,--会将命令行参数替换成set的命令行值)

set -- $(getopt -q -ab:cd "$@")

脚本示例如下:

[root@localhost test]# vi test4.sh
#!/bin/bash
#Extract command line options & values with getopt
#
set -- $(getopt -q ab:cd "$@")
while [ -n "$1" ]
do
        case "$1" in
                -a) echo "Found the -a options";;
                -b) parameter=$2
                    echo "Found the -b options with parameter value $parameter"
                    shift;;
                -c) echo "Found the -c options";;
                -d) echo "Found the -d options";;
                --) shift
                    break;;
                *) echo "$1 is not option";;
        esac
        shift
done
#
count=1
for parameter in "$@"
do
        echo "The #$parameter is a parameter"
        count=$[ $count + 1 ]
done
~                                                                                                                                                                                                 
"test4.sh" 26L, 516C written
[root@localhost test]# ./test4.sh -a -b test1 -c -d test3 test4 test5 test6
Found the -a options
Found the -b options with parameter value 'test1'
Found the -c options
Found the -d options
The #'test3' is a parameter
The #'test4' is a parameter
The #'test5' is a parameter
The #'test6' is a parameter

根据上述可看出getopt命令可以自动识别参数、选项,无需使用--来隔开参数。

但是getopt存在缺陷,例如同样运行上述脚本执行以下参数,结果可能会出现偏差

示例:

[root@localhost test]# ./test4.sh -a -b "test1 test2"  -cd test3 test4
Found the -a options
Found the -b options with parameter value 'test1
test2' is not option    #test1和test2同为-b选项的参数,但仅能识别test1,getopt不能识别""
Found the -c options
Found the -d options
The #'test3' is a parameter
The #'test4' is a parameter

通过上述脚本执行结果可发现getopt不能识别“”,用“”引起来的参数不会被当成同一个参数来对待。


3.5 getopts

getopts命令相当于getopt命令的升级版,但是getopts和getopt存在乳如下差异:

(1)getopt会将所有输入选项和参数一次性输出,但是getopts一次只处理一个参数或选项,输出完成后会返回一个大于0的退出码;

(2)getopts忽略错误的方式是在OPTstring之前加:,而getopt忽略错误的方式是加-q选项

命令格式: getopts OPTSTRING parameter

(3)getopts会将“”下的参数当成一个参数来输出

(4)getopts会自动移除--选项,所以case中的条件无需指定--

(5)getopts可以将选项和参数输入到一起,无需空格隔开

(6)对于未识别的选项和参数会返回?

(7)getopts命令会用到两个环境变量。如果选项需要跟一个参数值,OPTARG环境变量就会保存这个值。OPTIND环境变量保存了参数列表中getopts正在处理的参数位置。这样你就能在处理完选项之后继续处理其他命令行参数

示例1:

[root@localhost test]# vi test5.sh
#!/bin/bash
#simple demonstration of the getopts command
#
while getopts :ab:c 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 "Unknow option : $opt ";;
        esac
done
~                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
"test5.sh" 12L, 271C written
[root@localhost test]# ./test5.sh -a -b test1 -c
Found the -a option
Found the -b option with VALUE test1
Found the -c option
[root@localhost test]# ./test5.sh -a -b "test1 test2" -c
Found the -a option
Found the -b option with VALUE test1 test2
Found the -c option
[root@localhost test]# ./test5.sh -d
Unknow option : ?


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

示例:

[root@localhost test]# vi test6.sh
#!/bin/bash
#Processing options & parameters with getopts
#
while getopts :ab:cd opt
do
        case "$opt" in
                a) echo "Found the -a option";;
                b) echo "Found the -b option with parameter is $OPTARG";;
                c) echo "Found the -c option";;
                d) echo "Found the -d option";;
                *) echo "Unknow options";;
        esac
done
shift $[ $OPTIND - 1 ]
#
count=1
for parameter in "$@"
do
        echo "Parameter #$count : $parameter"
        count=$[ $count + 1 ]
done
~                                                                                                                                                                                                 
"test6.sh" 21L, 431C written
[root@localhost test]# ./test6.sh -a -b test1 -c -d test3 test4
Found the -a option
Found the -b option with parameter is test1
Found the -c option
Found the -d option
Parameter #1 : test3
Parameter #2 : test4


3.6 选项标准化

linux中有一些比较通用的选项,如果我们的脚本支持这些选项对用户来说将变的更友好

-a : 显示所有对象

-c : 生成一个计数器

-d  :  指定一个目录

-e : 扩展一个对象

-f  : 指定一个输入数据的文件

-h : 显示该命令的帮助信息

-i  : 忽略文本大小写

-l  : 输出长格式版本

-n : 使用非交互模式输出(批处理)

-o : 将输出内容重定向输出至指定文件

-q : 以安静模式运行

-r : 递归的处理目录和文件

-s : 以安静模式运行

-v :生成详细输出

-x : 排除某个对象

-y : 对所有问题都回答YES


4. 获得用户的输入

4.1 获得用户的基本输入

可以使用read命令获得用户的输入

示例

[root@localhost test]# vi test7.sh
#!/bin/bash
#Testing the read command
#
echo -n "Enter your name:"
read name
echo "Hello $name , welcome to my program ! "
~                                                                                                                                                                                                 
~                                                                                                 
"test7.sh" [New] 6L, 123C written
[root@localhost test]# chmod o+x test7.sh    
[root@localhost test]# ./test7.sh
Enter your name:liuxingyue
Hello liuxingyue , welcome to my program !

根据上述脚本可看出read 可以将接受用户输入的参数赋予给read命令后的变量,echo后的-n参数表示在字符串末尾输出换行符;

初此之外read 还可以使用-p命令接受用户输入的参数

示例

[root@localhost test]# vi test8.sh
#!/bin/bash
#Testing "read -p" command
#
read -p "Please input your age :" age
day=$[ $age * 365 ]
echo "That makes your over $day days old !" 
~                                                                                                                                                                                                  
"test8.sh" 6L, 144C written
[root@localhost test]# ./test8.sh 
Please input your age :33
That makes your over 12045 days old !

上述示例处理的只是单个变量,如果用户输入多个参数,read可以将参数依次匹配到指定变量,如果输入参数个数比变量个数多,则会将最后的参数全部匹配到最后的变量中

示例

[root@localhost test]# vi test9.sh
#!/bin/bash
# ENtering multiple variables
#
read -p "Enter your name :" first last
echo "Welcome $first $last..."
~                                                                                                                                                                                                
~                                                                                                 
"test9.sh" [New] 5L, 114C written
[root@localhost test]# chmod o+x test9.sh
[root@localhost test]# ./test9.sh
Enter your name :liu xingyue hehe
Welcome liu xingyue hehe...

read命令会将用户输入的参数存入到REPLY变量中,如果read不指定变量,使用$REPLY变量也可以。

示例

[root@localhost test]# ./test10.sh
Enter your name :wangyanglin ^H
wangyanglin , Wlecome to my program
[root@localhost test]# vi test10.sh
#!/bin/bash
#Testing $REPLY
#
read -p "Enter your name :"
echo "$REPLY , Wlecome to my program"
~                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
~                                                                                                                                                                                                                                           
"test10.sh" 5L, 97C written
[root@localhost test]# ./test10.sh
Enter your name :hah heh hoh
hah heh hoh , Wlecome to my program
[root@localhost test]# ^C
[root@localhost test]#


4.2 输入超时控制

read -t  #命令通过-t选项指定一个计数器,#表示秒数,加入#为5,则表示5秒之内如果用户没有输入会自动退出脚本并返回一个非零的退出状态码

示例:

[root@localhost test]# vi test11.sh
#!/bin/bash
#Test the -t option
#
read -t 5 -p "Enter your name :" 
echo "$REPLY , Welcome to my program"
~                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
~                                                                                                                                                                                                                                           
~                                                                                                                                                                                                                                           
"test11.sh" [New] 5L, 105C written
[root@localhost test]# chmod o+x test11.sh
[root@localhost test]# ./test11.sh
Enter your name : , Welcome to my program

read -n# 选项可以定义输入的字符个数,#为1,则表示仅只能输入一个字符。

[root@localhost test]# vi test12.sh
#!/bin/bash
#getting just one character of input
read -n1 -p"Do you want contine [Y/N] ?"
case $REPLY in
        y|Y) echo "Fine,contine to .......";;
        n|N) echo "OK , bye !"
             exit;;
        *) echo "Input error"
           exit;;
esac
echo "This is the end of the script"
~                                                                                                                                                                                                 
"test12.sh" 11L, 257C written
[root@localhost test]# ./test12.sh 
Do you want contine [Y/N] ?yFine,contine to .......
This is the end of the script
[root@localhost test]# ./test12.sh 
Do you want contine [Y/N] ?nOK , bye !
[root@localhost test]# ./test12.sh 
Do you want contine [Y/N] ?eInput error


4.3 隐藏式输入

    read命令的 -s参数可以实现输入隐藏,脚本运行时你输入的内容不会显示出来(实际上是显示的,只是颜色与背景色一样),一般会用于输入密码或者其他场景。

示例:

[root@localhost test]# vi test13.sh
#!/bin/bash
#Testing the "-s" option.
#
read -s -p "Please input your password:" pass
echo "Your password is $pass"
~                                                                                                 
~                                                                                                                                                                                                  
"test13.sh" 5L, 116C written
[root@localhost test]# ./test13.sh 
Please input your password:Your password is nihao


4.4 read读取文本输入,可以使用while循环,文本输入完成以后,自动结束命令

示例:

[root@localhost test]# vi test14.sh
#!/bin/bash
#Testing text input with read command
#
count=1
cat test | while read line
do
        echo "Line #$count : $line"
        count=$[ $count +1 ]
done
echo "Finished processing the file."
~                                                                                                 
~                                                                                                                                                                                                  
~                                                                                                 
"test14.sh" [New] 10L, 183C written
[root@localhost test]# cat test
ck brown dog jumps over the lazy fox.
This is a test, this is only a test.
O Romeo, Romeo! Wherefore art thou Romeo?
[root@localhost test]# ./test14
-bash: ./test14: No such file or directory
[root@localhost test]# ./test14.sh
Line #1 : ck 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.