# 简介
我们之前只是讲了脚本如何输出,也就是 echo
,本文会讲解脚本如何处理用户的输入。同时我们会涉及到另外一些变量的讲解。
# 位置参数
我们之前使用过许多命令,他们有些可以接收若干个参数,这其实是通过位置参数来实现的。
$0
是程序名, $1~$9
是第 1~9
个参数,超过 9 时,就要使用 ${}
,比如 ${10}
,这样,就允许在运行时加入任意个参数。
cyan@cyan-virtual-machine:~/Templates$ cat tmp.txt | |
#/bin/bash | |
echo $0 | |
echo $1 | |
echo $2 | |
cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt var1 var2 | |
./tmp.txt | |
var1 | |
var2 |
$0
是命令行输入的程序名,以绝对 (相对) 路径运行, $0
就是绝对 (相对) 路径。该特性会对脚本造成不同程度的影响,所以可以使用 basename
命令,只会返回不包含路径的脚本名: name=$(basename $0)
$()
是命令替换。
注:
- 在位置参数使用时,有可能并没有输入这个参数,所以在使用前需要测试参数:
if [ -n "$1" ]
测试变量是否存在, 必须要加引号。 $#
是含有脚本运行时携带的命令行参数个数,$@
包含所有参数,当作同一字符串多个独立单词,$*
是同一字符串,所有参数看作一个单词。- 位置变量中,想要将数字使用变量代替 (比如
$1
):$(!var)
其中 var 值为 1,${!#}
表示最后一个参数,${}
中不能使用$
而是使用!
代替。
这几个参数的使用和上述例子差不多,此处不再赘述。
# 移动变量
shift
命令会默认将所有位置参数都向左移动一位,也就是 $1
的值变成了 $2
。 $0
的值始终不会变。使用 shift 是遍历位置参数的一个好方法
#!/bin/bash | |
# demonstrating the shift command | |
echo | |
count=1 | |
while [ -n "$1" ] | |
do | |
echo "Parameter #$count = $1" | |
count=$[ $count + 1 ] | |
shift | |
done |
也可以给 shift 传递一个参数,指明参数移动的距离。
# getopt 命令
说实话,这部分当时书上讲得挺让我难受的,痛苦.jpg。
getopt
命令可以接受一系列任意形式的命令行选项和参数,并将它们转换成适当的格式。说白了,该函数就是用来解析命令行选项参数的。
使用: getopt optstring parameters
。
optstring
定义了命令可以接受的命令行有效的选项字母(要分析的参数),在每个需要参数值的选项字母后面加一个冒号,其参数紧跟在选项后面或者隔一个空格;如果选项字母后面加了 2 个冒号,则参数和选项之间不能有空格。 getopt
会基于定义的 optstring
解析提供的参数。
paramters
是实际的参数列表。
# 举个例子 | |
cyan@cyan-virtual-machine:~$ getopt ab:cd -a -b test1 -cd test2 test3 | |
-a -b test1 -c -d -- test2 test3 |
上面例子可以看到, optstring
是 ab:cd
,规定了命令可以接受的有效的选项 a,b,c,d
。 b
的后面有冒号,所以 -b
选项后面必须跟一个参数,例子中是 test1
。 -a -b test1 -cd test2 test3
是 parameters
,里面出现的选项不能是 optstring
之外的选项。
注意,它会自动将 -cd
选项分为两个单独的选项,并插入双破折号来分隔行中的额外参数。
为了帮你更好理解这个命令,我们会写一个脚本,但是在这之前,我希望你能知道 set
命令。该命令会将位置参数设置为固定的值
cyan@cyan-virtual-machine:~/Templates$ cat new_File | |
#!/bin/bash | |
echo $1 $2 $3 | |
set -- 参数1 参数2 参数3 | |
echo $1 $2 $3 | |
cyan@cyan-virtual-machine:~/Templates$ ./new_File para1 para2 para3 | |
para1 para2 para3 | |
参数1 参数2 参数3 |
现在写一个脚本
#!/bin/bash | |
#set -- 会将命令行参数替换成 set 命令的命令行值,挺重要的 | |
#-q 可以去掉报错信息,$@是得到脚本后面跟着的参数(字符串形式,同一个字符串多个单词) | |
set -- $(getopt -q ab:cd "$@") | |
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 |
所以,我们平时使用的命令的选项 + 参数,其原理差不多就是这样的(当然不排除更复杂的)。(⊙﹏⊙),我觉得加上这个脚本例子,你应该很明白 getopt
到底是干什么的了吧。
# getopts 命令
没错,我当时也很难受,费工夫理解 getopt
之后,又来一个。
该命令内建于 bash shell
,比 getopt
多了一些扩展功能。使用格式为: getopts optstring variable
每次只处理命令行一个参数,处理完所有参数后,就会退出并返回一个大于 0 的退出码。
- 如果要想
getiopt -q
那样去掉错误信息,需要在optstring
前加上一个冒号。 getopts
命令会将当前参数(选项)保存在命令行中定义的variable
中getopts
命令会用到两个环境变量。选项如果需要一个参数值,OPTARG
环境变量就会保存这个值。OPTIND
环境变量保存参数列表中getopts
正在处理的参数位置。
#!/bin/bash | |
echo "OPTIND的值为:$OPTIND" | |
# 根据第 2 点,当前选项会被保存在 opt 中 | |
while getopts :ab:c opt | |
do | |
case "$opt" in | |
# 我写的是 a 而不是 - a | |
a) echo "Found the -a option" ;; | |
b) echo "Found the -b option,with value $OPTARG" ;; # 选项后的参数值 | |
c) echo "Found the -c option" ;; | |
*) echo "Unkown option:$opt";; | |
esac | |
echo "OPTIND的值为:$OPTIND" | |
done |
如果我给的真实的参数列表为:
cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt -a -btest1 -c test2 test3 |
得到的结果就是
OPTIND的值为:1 | |
Found the -a option | |
OPTIND的值为:2 | |
Found the -b option,with value test1 | |
OPTIND的值为:3 | |
Found the -c option | |
OPTIND的值为:4 |
从该脚本中,我们看到了 getopts
如何根据两个系统变量进行参数控制的。我们不妨将脚本内容改一下
#!/bin/bash | |
echo "OPTIND的值为:$OPTIND" | |
# 根据第 2 点,当前选项会被保存在 opt 中 | |
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 "Unkown option:$opt";; | |
#esac | |
echo $opt | |
echo "OPTIND的值为:$OPTIND" | |
done |
参数列表不变
cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt -a -btest1 -c test2 test3 | |
OPTIND的值为:1 | |
a | |
OPTIND的值为:2 | |
b | |
OPTIND的值为:3 | |
c | |
OPTIND的值为:4 |
在 opt=c
之后还进行了一次循环,此时 opt 应该是到了其他参数( test2,test3
)那里,但是不会存储相应的值, opt
只存储选项的值。
# read 命令
read
命令用于获取用户输入:
read name
输入的数据就会保存在name
中。read -p "Please enter your age:" age
,-p
参数允许指定输入提示语句。read -p "Please enter your name:" firstname lastname
可以指定多个变量,如果输入大于变量个数,就会将剩下的输入全部保存在最后一个变量中。- 不指定保存变量时,会将输入保存在环境变量
REPLY
中。 read -t 5 age
设置超时选项,如果规定时间没有完成输入,就会继续执行,单位是秒。read -n1 -p "Please enter [Y/N] to continue:" ans
,可以通过-n
对输入长度进行限制,数字可以与选项-n
一起使用。给的例子中并且只要按下单个字符后,不需要按回车就会继续执行。read -s -p "Please enter the password:" passwd
,-s
会让输入时数据隐藏。
#!/bin/bash | |
# 从文件中读取,每次调用 read 命令就会读取一行文本,当文件没有内容时,read 就会退出并返回非 0 退出状态码 | |
# 文件数据传给 read,最常见的就是使用 cat,将结果通过管道直接传给含有 read 命令的 while 命令 | |
count=1 | |
cat data.txt | while read line | |
do | |
echo "Line $count:$line" | |
count=$[$count + 1] | |
done | |
echo "Finished proceessign the file" |
# 最后
请多加练习。