bash shell会将所有的命令行参数都指派给称作位置参数(positional parameter)的特殊变量。这也包括shell脚本名称。位置变量的名称都是标准数字:$0对应脚本名,$1对应第一个命令行参数,$2对应第二个命令行参数,以此类推,直到$9。
demo:
#!/bin/bash
product=$[ $1 * $2 ]
echo The first parameter is $1.
echo The second parameter is $2.
echo The product value is $product.
exit
结果:
PS D:\local_project\shell\14-处理用户输入\传递参数> sh test01.sh 2 5
The first parameter is 2.
The second parameter is 5.
The product value is 10.
如果脚本需要的命令行参数不止9个,则仍可以继续加入更多的参数,但是需要稍微修改一下位置变量名。在第9个位置变量之后,必须在变量名两侧加上花括号,比如${10}。来看一个例子:
#!/bin/bash
# Handling lots of command-line parameters
#
product=$[ ${10} * ${11} ]
echo The tenth parameter is ${10}.
echo The eleventh parameter is ${11}.
echo The product value is $product.
exit
$ ./positional10.sh 1 2 3 4 5 6 7 8 9 10 11 12
The tenth parameter is 10.
The eleventh parameter is 11.
The product value is 110.
可以使用位置变量$0获取在命令行中运行的shell脚本名。
编写一个脚本,生成能标识运行时间的日志消息:
# !/bin/bash
scriptname=$(basename $0)
echo The $scriptname ran at $(date) >> ./scripttrack.log
特殊变量$#含有脚本运行时携带的命令行参数的个数。你可以在脚本中的任何地方使用这个特殊变量,就跟普通变量一样:
# !/bin/bash
if [ $# -eq 1 ]
then
fragment="parameter was"
else
fragment="parameters were"
fi
echo $# $fragment supplied.
结果:
PS D:\local_project\shell\14-处理用户输入\特殊参数变量> sh .\test01.sh 1
1 parameter was supplied.
PS D:\local_project\shell\14-处理用户输入\特殊参数变量> sh .\test01.sh 1 11 1 11 1 1
6 parameters were supplied.
想获取用户传入的最后一个参数,得这么写:
# !/bin/bash
echo The number of parameters is $#
echo The last parameter is ${!#}
结果:
PS D:\local_project\shell\14-处理用户输入\特殊参数变量> sh .\test02.sh 1 2 6
The number of parameters is 3
The last parameter is 6
有时候你想要抓取命令行中的所有参数。这时无须先用$#变量判断有多少个命令行参数,然后再进行遍历,用两个特殊变量即可解决这个问题。
∗ 变量和 *变量和 ∗变量和@变量可以轻松访问所有参数,它们各自包含了所有的命令行参数。*
* ∗ 变量会将所有的命令行参数视为一个单词。这个单词含有命令行中出现的每一个参数。基本上, *变量会将所有的命令行参数视为一个单词。这个单词含有命令行中出现的每一个参数。基本上, ∗变量会将所有的命令行参数视为一个单词。这个单词含有命令行中出现的每一个参数。基本上,*变量会将这些参数视为一个整体,而不是一系列个体。另外,$@变量会将所有的命令行参数视为同一字符串中的多个独立的单词,以便你能遍历并处理全部参数。这通常使用for命令完成。
这两个变量的工作方式不太容易理解。下面来看一个例子,你就能明白二者之间的区别了:
# !/bin/bash
echo "Using the \$* method: $*"
count=1
for param in "$*"
do
echo "\$* Parameter #$count = $param"
count=$[ $count + 1 ]
done
echo "Using the \$@ method: $@"
count=1
for param in "$@"
do
echo "\$@ Parameter #$count = $param"
count=$[ $count + 1 ]
done
结果:
PS D:\local_project\shell\14-处理用户输入\特殊参数变量> sh .\test03.sh 1 2 3 4
Using the $* method: 1 2 3 4
$* Parameter #1 = 1 2 3 4
Using the $@ method: 1 2 3 4
$@ Parameter #1 = 1
$@ Parameter #2 = 2
$@ Parameter #3 = 3
$@ Parameter #4 = 4
bash shell工具箱中的另一件工具是shift命令,该命令可用于操作命令行参数。跟字面上的意思一样,shift命令会根据命令行参数的相对位置进行移动。
在使用shift命令时,默认情况下会将每个位置的变量值都向左移动一个位置。因此,变量$3的值会移入$2,变量$2的值会移入$1,而变量$1的值则会被删除(注意,变量$0的值,也就是脚本名,不会改变)。
这是遍历命令行参数的另一种好方法,尤其是在不知道到底有多少参数的时候。你可以只操作第一个位置变量,移动参数,然后继续处理该变量。
demo:
# !/bin/bash
echo "Using the shift method:"
count=1
while [ -n "$1" ]
do
echo "Parameter #$count = $1"
count=$[ $count + 1 ]
shift
done
接下来看复杂一点的使用案例,处理用户输入选项:
# !/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
结果:
PS D:\local_project\shell\14-处理用户输入\处理选项> sh .\test01.sh -a -b -- 1 2
Found the -a option
Found the -b option
Parameter #1: 1
Parameter #2: 2
# !/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
getopt命令可以接受一系列任意形式的命令行选项和参数,并自动将其转换成适当的格式。getopt的命令格式如下:
getopt optstring parameters
optstring是这个过程的关键所在。它定义了有效的命令行选项字母,还定义了哪些选项字母需要参数值。
首先,在optstring中列出要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后面加一个冒号。getopt命令会基于你定义的optstring解析提供的参数。
下面这个简单的例子演示了getopt是如何工作的:
$ getopt ab:cd -a -b BValue -cd test1 test2
-a -b BValue -c -d -- test1 test2
optstring定义了4个有效选项字母:a、b、c和d。冒号(:)被放在了字母b后面,因为b选项需要一个参数值。当getopt命令运行时,会检查参数列表(-a -b BValue -cd test1 test2),并基于提供的optstring进行解析。注意,它会自动将-cd分成两个单独的选项,并插入双连字符来分隔命令行中额外的参数。
read命令从标准输入(键盘)或另一个文件描述符中接受输入。获取输入后,read命令会将数据存入变量。下面是该命令最简单的用法:
# !/bin/bash
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my script."
实际上,read命令也提供了-p选项,允许直接指定提示符:
# !/bin/bash
read -p "Please enter your age: " age
days=$[ $age * 365 ]
echo "That means you are over $days days old!"
也可以在read命令中不指定任何变量,这样read命令便会将接收到的所有数据都放进特殊环境变量REPLY中:
# !/bin/bash
read -p "Enter your name: "
echo
echo "Hello $REPLY, welcome to my script."
使用read命令时要当心。脚本可能会一直苦等着用户输入。如果不管是否有数据输入,脚本都必须继续执行,你可以用-t选项来指定一个计时器。-t选项会指定read命令等待输入的秒数。如果计时器超时,则read命令会返回非0退出状态码:
# !/bin/bash
if read -t 5 -p "Enter your name: " name
then
echo "Hello $name, welcome to my script."
else
echo
echo "Sorry, no longer waiting for name."
fi
你也可以不对输入过程计时,而是让read命令统计输入的字符数。当字符数达到预设值时,就自动退出,将已输入的数据赋给变量:
#!/bin/bash
read -n 1 -p "Do you want to continue [Y/N]? " answer
#
case $answer in
Y | y) echo
echo "Okay. Continue on...";;
N | n) echo
echo "Okay. Goodbye"
exit;;
esac
我们也可以使用read命令读取文件。每次调用read命令都会从指定文件中读取一行文本。当文件中没有内容可读时,read命令会退出并返回非0退出状态码。
其中麻烦的地方是将文件数据传给read命令。最常见的方法是对文件使用cat命令,将结果通过管道直接传给含有read命令的while命令。来看下面的例子:
# !/bin/bash
count=1
cat line.txt | while read line
do
echo "Line $count: $line"
count=$[ $count + 1 ]
done
echo "Finished processing the file."