1命令行参数
1.1读取参数
bash shell会将一些称为位置参数(positional parameter)的特殊变量分配给输入到命令行中的所有参数。这也包括shell所执行的脚本名称。位置参数变量是标准的数字:$0是程序名,$1是第一个参数,$2是第二个参数,依次类推,直到第九个参数$9。
例子:计算阶乘
$ vim test1.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 $ ./test1.sh 5 The factorial of 5 is 120
1.2读取脚本名
以用$0参数获取shell在命令行启动的脚本名。
$ cat test5.sh #!/bin/bash # Testing the $0 parameter echo The zero parameter is set to: $0 $bash test5.sh The zero parameter is set to: test5.sh
但是这里存在一个潜在的问题。如果使用另一个命令来运行shell脚本,命令会和脚本名混在一起,出现在$0参数中。比如:
$ ./test5.sh The zero parameter is set to: ./test5.sh $ bash /home/Christine/test5.sh The zero parameter is set to: /home/Christine/test5.sh
这个时候basename命令可以帮助我们,basename命令会返回不包含路径的脚本名。
$ cat test5b.sh #!/bin/bash # Using basename with the $0 parameter name=$(basename $0) echo echo The script name is: $name $ bash /home/Christine/test5b.sh The script name is: test5b.sh $ ./test5b.sh The script name is: test5b.sh
可以用这种方法来编写基于脚本名执行不同功能的脚本。这里有个简单的例子:
$ cat test6.sh #!/bin/bash name=$(basename $0) #判断参数是否为空,如果为空的话(就是执行脚本时少带了参数),后面的脚本会报错 if [ -e $1 ] then echo "$1 is empty" exit 1 elif [ -e $2 ] then echo "$2 is empty" exit 1 fi if [ $name = "addem" ] then total=$[ $1 + $2 ] elif [ $name = "multem" ] then total=$[ $1 * $2 ] fi echo The calculated value is $total 开始测试: $ cp test6.sh addem $ chmod u+x addem $ ln -s test6.sh multem $ ls -l *em -rwxrw-r--. 1 Christine Christine 224 Jun 30 23:50 addem lrwxrwxrwx. 1 Christine Christine 8 Jun 30 23:50 multem -> test6.sh $ ./addem 2 5 The calculated value is 7 $ ./multem 2 5 The calculated value is 10
本例从test6.sh脚本中创建了两个不同的文件名:一个通过复制文件创建(addem),另一个通过链接创建(multem)。在两种情况下都会先获得脚本的基本名称,然后根据该值执行相应的功能。
2特殊参数变量
2.1参数统计
特殊变量$#:含有脚本运行时携带的命令行参数的个数
$ cat test8.sh #!/bin/bash # getting the number of parameters echo There were $# parameters supplied. $ ./test8.sh There were 0 parameters supplied. $ ./test8.sh 1 2 3 4 5 There were 5 parameters supplied. $ ./test8.sh 1 2 3 4 5 6 7 8 9 10 There were 10 parameters supplied. $ ./test8.sh "Rich Blum" There were 1 parameters supplied.
如果要直接获取最后一个参数的值时,不应该用${$#},而是应该用${!#}:
2.2抓取所有的数据
$*和$@变量可以用来轻松访问所有的参数。这两个变量都能够在单个变量中存储所有的命令行参数。
$*变量会将命令行上提供的所有参数当作一个单词保存,这个单词包含了命令行中出现的每一个参数值;
$@变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。
例子:
$ cat test10.sh #!/bin/bash # Grabbing the last parameter params=$# echo echo The last parameter is $params echo The last parameter is ${!#} $ bash test10.sh 1 2 3 4 5 The last parameter is 5 The last parameter is 5 $ bash test10.sh The last parameter is 0 The last parameter is test10.sh
3移动变量
shift命令能够用来操作命令行参数。它会根据相对位置来移动命令行参数。在使用shift命令时,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3的值会移到$2中,变量$2的值会移到$1中,而变量$1的值则会被删除(注意,变量$0的值,也就是程序名,不会改变)。
$ cat test13.sh #!/bin/bash count=1 while [ -n "$1" ] do echo "Parameter #$count = $1" count=$[ $count + 1 ] shift done $ ./test13.sh rich barbara katie jessica Parameter #1 = rich Parameter #2 = barbara Parameter #3 = katie Parameter #4 = jessica
另外,你也可以一次性移动多个位置,只需要给shift命令提供一个参数,指明要移动的位置数就行了。
$ cat test14.sh #!/bin/bash echo "The original parameters: $*" shift 2 echo "Here's the new first parameter: $1" $ ./test14.sh 1 2 3 4 5 The original parameters: 1 2 3 4 5 Here's the new first parameter: 3
4处理选项
1. 处理简单选项
$ cat test15.sh #!/bin/bash # extracting command line options as parameters 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 $ ./test15.sh -a -b -c -d Found the -a option Found the -b option Found the -c option -d is not an option
2. 分离参数和选项
双破折线(--):表明选项列表结束。
$ cat test16.sh #!/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=$[ $count + 1 ] done $ ./test16.sh -c -a -b test1 test2 test3 Found the -c option Found the -a option Found the -b option test1 is not an option test2 is not an option test3 is not an option $ ./test16.sh -c -a -b -- test1 test2 test3 Found the -c option Found the -a option Found the -b option Parameter #1: test1 Parameter #2: test2 Parameter #3: test3
3. 处理带值的选项
$ cat test17.sh #!/bin/bash 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 $ ./test17.sh -a -b test1 -d Found the -a option Found the -b option, with parameter value test1 -d is not an option
但是这种脚本遇到$ ./test17.sh -ac的命令九九没法执行了。
5将选项标准化
选 项 |
描 述 |
-a |
显示所有对象 |
-c |
生成一个计数 |
-d |
指定一个目录 |
-e |
扩展一个对象 |
-f |
指定读入数据的文件 |
-h |
显示命令的帮助信息 |
-i |
忽略文本大小写 |
-l |
产生输出的长格式版本 |
-n |
使用非交互模式(批处理) |
-o |
将所有输出重定向到的指定的输出文件 |
-q |
以安静模式运行 |
-r |
递归地处理目录和文件 |
-s |
以安静模式运行 |
-v |
生成详细输出 |
-x |
排除某个对象 |
-y |
对所有问题回答yes |
6获得用户输入
6.1基本的读取
read命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后,read命令会将数据放进一个变量
$ cat test22.sh #!/bin/bash # testing the read -p option read -p "Please enter your age: " age days=$[ $age * 365 ] echo "That makes you over $days days old! " $ ./test22.sh Please enter your age: 10 That makes you over 3650 days old!
6.2 超时
使用read命令时要当心。脚本很可能会一直苦等着脚本用户的输入。如果不管是否有数据输入,脚本都必须继续执行,你可以用-t选项来指定一个计时器。-t选项指定了read命令等待输入的秒数。当计时器过期后,read命令会返回一个非零退出状态码。
$ cat test25.sh #!/bin/bash # timing the data entry if read -t 5 -p "Please enter your name: " name then echo "Hello $name, welcome to my script" else echo echo "Sorry, too slow! " fi $ ./test25.sh Please enter your name: Rich Hello Rich, welcome to my script $ ./test25.sh Please enter your name: Sorry, too slow!
如果计时器过期,read命令会以非零退出状态码退出,可以使用如if-then语句或while循环这种标准的结构化语句来理清所发生的具体情况。
也可以不对输入过程计时,而是让read命令来统计输入的字符数。当输入的字符达到预设的字符数时,就自动退出,将输入的数据赋给变量。
$ cat test26.sh #!/bin/bash # getting just one character of input read -n1 -p "Do you want to continue [Y/N]? " answer case $answer in Y | y) echo "fine, continue on…";; N | n) echo "OK, goodbye" exit;; esac echo "This is the end of the script" $ ./test26.sh Do you want to continue [Y/N]? Y fine, continue on… This is the end of the script $ ./test26.sh Do you want to continue [Y/N]? n OK, goodbye
本例中将-n选项和值1一起使用,告诉read命令在接受单个字符后退出。只要按下单个字符回答后,read命令就会接受输入并将它传给变量,无需按回车键。