Shell脚本学习之旅(5)— 用户输入
一、命令行参数
向shell脚本传递数据的最基本方法是使用命令行参数。命令行参数允许在运行脚本时向命令行添加数据。
bash shell会将一些称为位置参数( positional parameter)的特殊变量分配给输入到命令行中的所有参数。这也包括shell所执行的脚本名称。位置参数变量是标准的数字: $0
是程序名, $1
是第一个参数, $2
是第二个参数,依次类推,直到第九个参数$9
。
编写脚本如下所示
#!/bin/bash
# testing two command line parameters
#
echo The first parameter is $1.
echo The second parameter is $2.
运行脚本可以看到
如果脚本需要的命令行参数不止9个,需要稍微修改一下变量名,在第9个变量之后,你必须在变量数字周围加上花括号,比如${10}
。
编写脚本如下所示
#!/bin/bash
# handling lots of parameters
#
total=$[ ${10} * ${11} ]
echo The tenth parameter is ${10}
echo The eleventh parameter is ${11}
echo The total is $total
运行如下所示
可以用$0
参数获取shell在命令行启动的脚本名,但是当传给$0变量的实际字符串不仅仅是脚本名,而是完整的脚本路径时,变量$0
就会使用整个路径。
编写脚本如下所示
#!/bin/bash
# Testing the $0 parameter
#
echo The zero parameter is set to: $0
运行如下所示
想要把脚本的运行路径给剥离掉,我们需要使用 basename
命令来返回不包含路径的脚本名
#!/bin/bash
# Using basename with the $0 parameter
#
name=$(basename $0)
echo
echo The script name is: $name
运行如下所示
当脚本认为参数变量中会有数据而实际上并没有时,脚本很有可能会产生错误消息。这种写脚本的方法并不可取。在使用参数前一定要检查其中是否存在数据。
编写脚本,使用了-n
测试来检查命令行参数$1
中是否有数据。
#!/bin/bash
# testing parameters before use
#
if [ -n "$1" ]
then
echo Hello $1, glad to meet you.
else
echo "Sorry, you did not identify yourself. "
fi
在添加和不添加参数两种情况下运行如下所示
二、特殊参数变量
在bash shell中有些特殊变量,它们会记录命令行参数。
特殊变量$#含有脚本运行时携带的命令行参数的个数。可以在脚本中任何地方使用这个特殊变量,就跟普通变量一样。
#!/bin/bash
# getting the number of parameters
#
echo There were $# parameters supplied.
运行如下所示
这样这个变量就提供了一个简便方法来获取命令行中最后一个参数,但是并不能使用变量${$#}
来代表了最后一个命令行参数变量,而是要将美元符换成感叹号。
#!/bin/bash
# Grabbing the last parameter
#
params=$#
echo
echo The last parameter is $params
echo The last parameter is ${$#}
echo The last parameter is ${!#}
echo
运行如下所示,可以看到,${$#}
并没有代表最后一个命令行参数变量,它表明你不能在花括号内使用美元符,需要注意的是,当命令行上没有任何参数时, $#
的值为0,params变量的值也一样,但${!#}
变量会返回命令行用到的脚本名。
有时候需要抓取命令行上提供的所有参数,可以使用$*
和$@
变量来解决这个问题,轻松访问所有的参数。
$*
变量会将命令行上提供的所有参数当作一个单词保存,这个单词包含了命令行中出现的每一个参数值。$@
变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。
编写脚本如下所示
#!/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
运行脚本,结果如下所示,$*
变量会将所有参数当成单个参数,而$@
变量会单独处理每个参数。
三、移动变量
bash shell
的shift
命令能够用来操作命令行参数, shift命令会根据它们的相对位置来移动命令行参数。
在使用shift命令时,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3的值会移到$2中,变量$2的值会移到$1中,而变量$1的值则会被删除(注意,变量$0的值,也就是程序名,不会改变)。
编写脚本如下所示
#!/bin/bash
# demonstrating the shift command
echo
count=1
while [ -n "$1" ]
do
echo "Parameter #$count = $1"
count=$[ $count + 1 ]
shift
done
运行结果如下所示,注意:使用shift命令的时候,如果某个参数被移出,它的值就被丢弃了,无法再恢复。
另外,也可以一次性移动多个位置,只需要给shift命令提供一个参数,指明要移动的位置数就行了。
#!/bin/bash
# demonstrating a multi-position shift
#
echo
echo "The original parameters: $*"
shift 2
echo "Here's the new first parameter: $1"
四、处理选项
getopt
命令是一个在处理命令行选项和参数时非常方便的工具。它能够识别命令行参数,从而在脚本中解析它们时更方便。
getopt命令可以接受一系列任意形式的命令行选项和参数,并自动将它们转换成适当的格式。它的命令格式如下:
getopt optstring parameters
optstring是这个过程的关键所在。它定义了命令行有效的选项字母,还定义了哪些选项字母需要参数值。
首先,在optstring
中列出你要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后加一个冒号。getopt
命令会基于你定义的optstring
解析提供的参数。
如图所示,因为在b
的后面添加了一个冒号,所以表示这个选项需要一个参数值,则处理后的-b
选项带了一个参数值。
可以在脚本中使用getopt来格式化脚本所携带的任何命令行选项或参数,但用起来略微复杂,方法是用getopt命令生成的格式化后的版本来替换已有的命令行选项和参数。用set命令能够做到。用getopt格式化后的命令行参数来替换原始的命令行参数,如下所示。
set -- $(getopt -q ab:cd "$@")
编写脚本如下所示
#!/bin/bash
# Extract command line options & values with getopt
#
set -- $(getopt -q 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
#
运行如下所示
五、将选项标准化
有些字母选项在Linux世界里已经拥有了某种程度的标准含义。如果你能在shell脚本中支持这些选项,脚本看起来能更友好一些。如下图所示,显示了Linux中用到的一些命令行选项的常用含义。
六、获得用户输入
尽管命令行选项和参数是从脚本用户处获得输入的一种重要方式,但有时脚本的交互性还需要更强一些。bash shell为此提供了read命令。
read命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后, read命令会将数据放进一个变量。下面是read命令的最简单用法。
#!/bin/bash
# testing the read command
#
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my program. "
#
运行如下所示,当运行到read
一行时,脚本会等待你的输入
另外,read命令包含-p选项,允许直接在read命令行指定提示符。
#!/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! "
#
使用read命令时要当心。脚本很可能会一直苦等着脚本用户的输入。如果不管是否有数据输入,脚本都必须继续执行,你可以用-t选项来指定一个计时器。 -t选项指定了read命令等待输入的秒数。当计时器过期后, read命令会返回一个非零退出状态码。
#!/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
运行如下所示
有时你需要从脚本用户处得到输入,但又在屏幕上显示输入信息。其中典型的例子就是输入的密码,但除此之外还有很多其他需要隐藏的数据类型。-s选项可以避免在read命令中输入的数据出现在显示器上(实际上,数据会被显示,只是read命令会将文本颜色设成跟背景色一样)。
#!/bin/bash
# hiding input data from the monitor
#
read -s -p "Enter your password: " pass
echo
echo "Is your password really $pass? "
运行如下所示
最后,也可以用read命令来读取Linux系统上文件里保存的数据。最常见的方法是对文件使用cat命令,将结果通过管道直接传给含有read命令的while命令。
#!/bin/bash
# reading data from a file
#
count=1
cat states| while read line
do
echo "Line $count: $line"
count=$[ $count + 1]
done
echo "Finished processing the file"
运行如下所示,while循环会持续通过read命令处理文件中的行,直到read命令以非零退出状态码退出。