1. 命令行参数
1.1 位置参数
bash shell会将一些称为位置参数(positional parameter)
的特殊变量分配给输入到命令行中的 所有参数。这也包括shell所执行的脚本名称。位置参数变量是标准的数字:$0
是程序名,$1
是第 一个参数,$2
是第二个参数,依次类推,直到第九个参数$9
。
读取文件名
可以用$0
参数获取shell在命令行启动的脚本名。
$ cat test5.sh
#!/bin/bash 11 # Testing the $0 parameter
#
echo The zero parameter is set to: $0
# 12 $
$ bash test5.sh
The zero parameter is set to: test5.sh
$
当传给$0
变量的实际字符串不仅仅是脚本名,而是完整的脚本路径时, 变量$0
就会使用整个路径。
$ bash /home/Christine/test5.sh
The zero parameter is set to: /home/Christine/test5.sh
$
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
$
1.2 特殊参数变量
$#
$*
$@
参数统计
特殊变量$#
含有脚本运行时携带的命令行参数的个数。
抓取所有的数据
$*
和$@
变量可以用来轻松访问所有的参数。
$*
变量会将命令行上提供的所有参数当作一个单词保存。
$@
变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。
下面的例子给出了二者的差异.
$ cat test12.sh
#!/bin/bash
# testing $* and $@
#
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
$ ./test12.sh rich barbara katie jessica
$* Parameter #1 = rich barbara katie jessica
$@ Parameter #1 = rich
$@ Parameter #2 = barbara
$@ Parameter #3 = katie
$@ Parameter #4 = jessica
$
2. 移动变量shift
在使用shift
命令时,默认情况下它会将每个参数变量向左移动一个位置
。
变量$3
的值会移到$2
中,变量$2
的值会移到$1
中,而变量$1
的值则会被删除(注意,变量$0
的值,也 就是程序名,不会改变)。
$ cat test14.sh
#!/bin/bash
# demonstrating a multi-position shift
#
echo
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
$
3. 处理选项(getopt、getopts)
选项是跟在单破折线后面的单个字母,它能改变命令的行为。
3.1 使用getopt命令
getopt命令可以接受一系列任意形式的命令行选项和参数,并自动将它们转换成适当的格 式。它的命令格式如下:
getopt optstring parameters
下面是个getopt如何工作的简单例子。
$ getopt ab:cd -a -b test1 -cd test2 test3 -a -b test1 -c -d -- test2 test3
$
optstring定义了四个有效选项字母:a、b、c和d。冒号(:
)被放在了字母b后面,因为b
选项需要一个参数值。
如果指定了一个不在optstring中的选项
,默认情况下,getopt命令会产生一条错误消息
。
$ getopt ab:cd -a -b test1 -cde test2 test3 getopt: invalid option -- e
-a -b test1 -c -d -- test2 test3
$
在脚本中使用getopt,配合set命令
方法是用getopt命令生成的格式化后的版本来替换已有的命令行选项和参数。用set命令能 够做到。
在第6章中,你就已经见过set命令了。set命令能够处理shell中的各种变量。
set命令的选项之一是双破折线(–),它会将命令行参数替换成set命令的命令行值。 然后,该方法会将原始脚本的命令行参数传给getopt命令,之后再将getopt命令的输出传给set命令,用getopt格式化后的命令行参数来替换原始的命令行参数,看起来如下所示。
set -- $(getopt -q ab:cd "$@")
现在原始的命令行参数变量的值会被getopt命令的输出替换,而getopt已经为我们格式化 好了命令行参数。
利用该方法,现在就可以写出能帮我们处理命令行参数的脚本。
$ cat test18.sh
#!/bin/bash
# Extract command line options & values with getopt #
set -- $(getopt 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 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
# $
现在如果运行带有复杂选项的脚本,就可以看出效果更好了。
$ ./test18.sh -ac
Found the -a option
Found the -c option
$
当然,之前的功能照样没有问题。
$ ./test18.sh -a -b test1 -cd test2 test3 test4
Found the -a option
Found the -b option, with parameter value 'test1'
Found the -c option
Parameter #1: 'test2'
Parameter #2: 'test3'
Parameter #3: 'test4'
$
3.2 使用getopts命令
如果选项需要跟一个参数值,OPTARG
环境变量就会保存这个值。
OPTIND
环境变量保存了参数列表中getopts正在处理的参数位置。
$ cat test20.sh
#!/bin/bash
# Processing options & parameters with getopts #
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" ;;
d) echo "Found the -d 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
$
$ ./test20.sh -a -b test1 -d test2 test3 test4
Found the -a option
Found the -b option, with value test1
Found the -d option
Parameter 1: test2
Parameter 2: test3
Parameter 3: test4
$
3.3 将选项标准化
在创建shell脚本时,显然可以控制具体怎么做。你完全可以决定用哪些字母选项以及它们的 用法。
但有些字母选项在Linux世界里已经拥有了某种程度的标准含义。如果你能在shell脚本中支 6 持这些选项,脚本看起来能更友好一些。
4. 获取用户输入
4.1 基本读取
read命令包含了-p
选项,允许你直接在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!
$
也可以在read命令行中不指定变量。如果是这样,read命令会将它收到的任何数据都放进 特殊环境变量REPLY
中。
$ cat test24.sh
#!/bin/bash
# Testing the REPLY Environment variable #
read -p "Enter your name: "
echo
echo Hello $REPLY, welcome to my program. #
$
$ ./test24.sh
Enter your name: Christine
Hello Christine, welcome to my program.
$
4.2 超时
你可以用-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!
$
4.3 限制输入字符个数
-n
选项限制个数
$ 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
echo "fine, continue on...";;
N | n) echo
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
$
4.4 隐藏方式读取
-s
选项可以避免在read命令中输入的数据出现在显示器上(实际上,数据会被显示,只是 read命令会将文本颜色设成跟背景色一样)。这里有个在脚本中使用-s
选项的例子。
$ cat test27.sh
#!/bin/bash
# hiding input data from the monitor
#
read -s -p "Enter your password: " pass
echo
echo "Is your password really $pass? "
$
$ ./test27.sh
Enter your password:
Is your password really T3st1ng?
$
4.5 从文件中读取
最常见的方法是对文件使用cat命令,将 结果通过管道直接传给含有read命令的while命令。下面的例子说明怎么处理。
$ cat test28.sh #!/bin/bash
# reading data from a file #
count=1
cat test | while read line do
echo "Line $count: $line"
count=$[ $count + 1]
done
echo "Finished processing the file"
$
$ cat test
The quick brown dog jumps over the lazy fox. This is a test, this is only a test.
O Romeo, Romeo! Wherefore art thou Romeo?
$
$ ./test28.sh
Line 1: The quick 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
$
while循环会持续通过read命令处理文件中的行,直到read命令以非零退出状态码退出。