首页 > 编程笔记 > Linux笔记 阅读:10

Linux位置参数变量详解(附带实例)

Linux 中,用户自定义变量和环境变量的赋值方式是相同的(只是环境变量需要进行额外的 export声明)。不同点在于,用户自定义变量只能在当前 shell 中调用,环境变量可以在当前及基于当前的子 shell 中调用。

因为用户自定义变量的值只能在当前 shell 中调用,所以在我们执行脚本时很难在脚本中调用用户自定义变量的值(直接将赋值过程写在脚本中又失去了变量灵活变化的意义)。

如果使用位置参数变量,就能够在脚本执行前将变量值传入脚本并执行,但需要注意的是,位置参数变量的赋值方式和用户自定义变量的赋值方式并不相同。

写在脚本程序之后的字符串就是位置参数变量的值。在执行脚本时,脚本名后的字符会传入脚本,并按照出现在脚本后字符串的先后位置以 $1、$2、$3 等特定变量名命名。

接下来,我们编写一个简单的脚本,主要观察位置参数变量的赋值和调用过程:
[root@localhost ~]# ls -l /root/ppv1.sh
-rwxr-xr-x. 1 root root 20 Dec  8 16:19 /root/ppv1.sh
#ppv1 为位置参数变量测试文件,已设置可执行权限
[root@localhost ~]# cat /root/ppv1.sh
#!/bin/bash

echo "$1"
#文件当中除了表明解释器类型,只能用 echo 进行输出变量$1 的值
[root@localhost ~]# /root/ppv1.sh A
A
在脚本后输入字母 A,为变量 $1 赋值,输出变量 $1 的值为 A。如果在脚本名后(以空格隔开)输入字符,那么字符将会赋值给变量 $1,最终 echo 可以将赋值后的 $1 的值输出。

大家可以结合下图来加深对于位置参数变量的理解。


图 1 单个变量单个值

若执行 /root/ppv1.sh 时在脚本名后加入字符,则脚本名后的字符 A 就会赋值给变量 $1,并随着 echo 输出显示到命令行中:
[root@localhost ~]# /root/ppv1.sh
#若直接执行ppv1.sh脚本,则输出结果为空行,因为此时位置参数变量没有取到任何值

若执行 /root/ppv1.sh 时不加任何字符串,则变量 $1 取值为空,echo 输出结果为空,如下图所示。


图 2 空值位置参数变量

写在脚本名后的可以是单个字符,也可以是连续的字符串,字符串执行结果如下:
[root@localhost ~]# cat /root/ppv1.sh
#!/bin/bash
echo $1
[root@localhost ~]# /root/ppv1.sh ABC
ABC

连续的字符串 ABC 赋值给变量 $1,然后通过 echo 输出到屏幕上,如下图所示:


图 3 单个变量多个值

因为是连续的字符串,所以 ABC 以整体的形式赋值给了变量 $1,并进行了输出。

我们已经见到连续字符串的赋值了,如果是并不连续的字符串会怎么样呢?结果如下:
[root@localhost ~]# cat /root/ppv1.sh
#!/bin/bash
echo $1
[root@localhost ~]# /root/ppv1.sh A B C
A
#在变量名后写入ABC三个字母,字母之间以空格隔开
最终,我们看到的结果是输出了字母 A,那么 B 和 C 呢?在位置参数变量中,写在脚本后的第一个连续的字符串会赋值给$1,但如果以空格隔开,出现的字符串就将会赋值给 $2,并以此类推,如下图所示。


图 4 多个变量多个值

在执行 /root/ppv1.sh 脚本时,在脚本名中写入 A、B、C 三个字符且三个字符之间用空格隔开。此时,因为三个字符外面并没有将它们表示为整体的双引号或单引号,所以这三个字符分别对 $1、$2、$3 赋值。对于当前脚本而言,只有 echo 输出了 $1 的值,因此只输出了字母 A。

我们可以在增加位置参数值的同时,增加相应数量的位置参数变量,脚本修改如下:
[root@localhost ~]# cat /root/ppv1.sh
#!/bin/bash
echo $1
echo $2
echo $3
#对应增加$2 和$3
[root@localhost ~]# /root/ppv1.sh A B C
A
B
C
#A、B、C 三个值正常输出
当前的 A、B、C 输出结果是分三行输出的。原因在于,我们每个位置参数变量都分别使用 echo 进行了输出,而 echo 输出每次都会默认换行。

接下来,如果我们在 A、B、C 外添加双引号,那么即便 A、B 与 C 之间有空格存在,ABC 也会以整体的方式进行赋值,执行结果如下:
[root@localhost ~]# cat /root/ppv1.sh
#!/bin/bash
echo $1
echo $2
echo $3
[root@localhost ~]# /root/ppv1.sh "A B C"
A B C
# “ABC” 以整体赋值给$1后通过echo进行输出,$2和$3赋值为空,输出空白行

现在,我们已经对位置参数变量的设置已经有了基本了解。接下来,我们尝试使用位置参数变量来编写一个能实现四则运算的计算器脚本。
[root@localhost ~]# ls -l /root/count2.sh
-rwxr-xr-x. 1 root root 57 Dec  9 00:49 /root/count2.sh
[root@localhost ~]# cat /root/count2.sh
#!/bin/bash
echo "$1$2$3 的计算结果是:$(($1$2$3))"
[root@localhost ~]# /root/count2.sh 1 2
1+2 的计算结果是:3
在 count2 脚本中,位置参数变量 $1 取值为 1,位置参数变量 $2 取值为 +,位置参数变量 $3 取值为 2,经过计算得到的结果为 3。

这样一来,我们只需在脚本后写明数字和运算符号,数字和运算符号就会传进脚本中进行赋值并计算了。

四则运算中的乘法用“*”来表示,但是“*”本身具有特殊含义,因此在计算乘法运算时记得加入“\”,表示要取消“*”的特殊含义,执行效果如下:
[root@localhost ~]# /root/count2.sh 2 * 3
/root/count2.sh: line 2: zanaconda: value too great for base (error token is "zanaconda")
#因为“*”默认存在特殊含义,所以直接运算会报错
[root@localhost ~]# /root/count2.sh 2 \* 3
2*3 的计算结果是:6
#使用“\”取消“*”的特殊含义,运算正常

在我们能够正常进行四则整数运算之后,接下来会发现想一次性计算多个数字的加减乘除还是不能成功,计算结果如下:
[root@localhost ~]# cat /root/count2.sh
#!/bin/bash
echo "$1$2$3 的计算结果是:$(($1$2$3))"
[root@localhost ~]# /root/count2.sh 1 + 2 + 3 + 4
1+2 的计算结果是:3

在脚本后输入字符串,表示要连续计算 1+2+3+4 的结果,但因为在脚本中只调用了 $1、$2、$3 三个变量的值,所以计算结果并不准确。这也是我们在使用位置参数变量过程中一个比较常见的问题,即我们可能无法确认执行脚本时到底有多少个位置参数变量需要赋值。

其实,在位置参数变量中不是只有 $n(n 表示数字)这一种取值方式,还有如 $* 或 $@ 等取值方式。

接下来具体讲解位置参数变量中都有哪些取值方式,可用位置参数变量如下表所示。

位置参数变量 作用
$n n 为数字,$0 代表命令本身,$1~$9 代表第 1 个至第 9 个参数,10 以上的参数需要用大括号包含,如 ${10};
$* 代表命令行中所有的参数,$* 把所有的参数看成一个整体
$@ 代表命令行中所有的参数,$@ 把每个参数都区别对待
$# 代表命令行中所有参数的个数

通过上表可以得知,使用 $* 或 $@ 都可以表示命令行中的所有位置参数,存在的只是当作整体和区分对待的差别。那么,我们分别使用 $* 和 $@ 执行计算器脚本,结果会如何呢?脚本修改后效果如下:
[root@localhost ~]# cat /root/count3.sh
#在原有的/root/count2.sh基础上修改脚本,使用$*
#!/bin/bash
echo "$* 的计算结果是:$(($*))"
[root@localhost ~]# /root/count3.sh 1 + 2 + 3 + 4
1+2+3+4 的计算结果是:10
从命令执行结果来看,使用 $* 可以将命令行中的所有字符赋值给 $* 并进行运算,运算结果正确。

接下来,我们看看使用 $@ 取值运算结果会如何:
[root@localhost ~]# cat /root/count4.sh
#!/bin/bash
echo "$@ 的计算结果是:$(($@))"
[root@localhost ~]# /root/count4.sh 1 + 2 + 3 + 4
1+2+3+4 的计算结果是:10
我们会发现,使用 $@ 同样取到了命令行中的所有值,并进行了正确的运算。仅论结果,/root/count3.sh和/root/count4.sh 两个脚本的执行结果并无差别,使用 $* 或 $@ 都是取所有值。

那么,$* 中把所有值当成整体,与 $@ 中把每个值区分对待,二者的差别要怎样才能看到呢?如果想观察 $* 和 $@ 之间的差别,就需要使用 for 循环。

首先,我们简单熟悉一下 for 循环的语句格式及赋值方式:
[root@localhost ~]# /root/for1.sh
for in 值 1 值 2 值 3
do
    赋值后执行
done
在 for1.sh 脚本中,for、in、do、done 是 for 循环的固定格式。其中,出现在 for 和 in 之间的是变量名。变量名是可以自定义的,这里我们使用 i 作为变量名。出现在 in 之后的是变量的值,在 in 之后可以出现一个值或多个值。如果是一个值,就将这个值赋值给变量 i,然后执行 do 和 done 之间的代码,之后脚本结束。如果在 in 之后有多个值,那么这些值会依次赋值给变量 i,并依次执行 do 和 done 之间的代码,如下图所示。


图 5 位置参数变量循环赋值

在图 5 中,在 for 循环中变量名为 i,变量的值分别是 A、B、C。脚本执行后,首先将值 A 赋值给变量 i,然后执行 do 和 done 之间的 echo,输出i的值。

其次,将值 B 赋值给变量 i。因为变量具有覆盖的特点,所以此时变量 i 的值是字母 B,再次执行 do 和 done 之间的 echo,输出 i 的值。

最后,将值 C 赋值给变量 i,然后执行 do 和 done 之间的 echo,输出 i 的值。此时,in 之后的值取尽,脚本执行结束。

接下来我们执行脚本并观察执行结果:
[root@localhost ~]# cat /root/for1.sh
#!/bin/bash
for i in A B C
do
    echo "$i"
done
#脚本内容
[root@localhost ~]# /root/for1.sh
A
B
C
#脚本执行后输出结果
在 for 循环中,出现在 in 之后的字符串,其内部以空格隔开,且在没有被双引号或单引号包含的情况下,字符串就被空格分隔成多个值,例如,ABC 会被认为是三个值。但是,如果字符串之间有空格但字符串整体被双引号或单引号包含,那么引号范围内的字符会被认为是一个整体,将使用这个整体进行赋值,例如,“ABC”会被认为是一个整体,将使用“ABC”整体进行赋值,执行效果如下:
[root@localhost ~]# cat /root/for1.sh
#!/bin/bash
for i in "A B C"
do
    echo "$i"
done
#在 "A B C" 外侧加入双引号,将其表示为整体
[root@localhost ~]# /root/for1.sh
A B C
#脚本执行后输出结果
因为使用了双引号将 ABC 表示为整体,所以在脚本执行时会一次性将“ABC”赋值给变量 i。在执行 echo 时,输出 ABC,此时所有值取尽,脚本执行结束。

出现在 for 循环中的值可以固定值的方式写入脚本,也可以使用如位置参数变量等方式将值传递进脚本。例如,可以使用位置参数变量中的 @ 和 *,将变量的值传给 for 循环执行,脚本如下:
[root@localhost ~]# cat /root/for2.sh
#!/bin/bash
for i in "$*"
do
    echo "$i"
done
#for 循环,使用$*位置参数取值

[root@localhost ~]# /root/for2.sh A B C
A B C
#通过输出结果可以得知,ABC 作为整体进行了赋值,进行了一次输出

[root@localhost ~]# cat /root/for3.sh
#!/bin/bash
for i in "$@"
do
    echo "$i"
done
#for 循环,使用$@位置参数取值

[root@localhost ~]# /root/for3.sh A B C
A
B
C
#通过输出结果可以得知,ABC 作为个体分别进行了赋值,进行了三次输出
通过 for 循环能发现,$* 和 $@ 都表示所有位置参数,但是 $* 将值当作整体,一次性将所有值进行了输出;$@ 将值作为多个独立的个体分别赋值,输出多次。

需要注意的是,大家需要在脚本中将 $* 和 $@ 用双引号包含起来,否则 $* 将值传进脚本后会被识别为多个个体,无法实现实验效果。

接下来,我们来看 $# 的作用,$# 用来表示位置参数的个数:
[root@localhost ~]# cat /root/ppv2.sh
#!/bin/bash
echo "$1"
#输出第一个位置参数变量的值
echo "$2"
#输出第二个位置参数变量的值
echo "$3"
#输出第三个位置参数变量的值
echo "$4"
#输出第四个位置参数变量的值
echo "$#"
#输出位置参数的个数
[root@localhost ~]# /root/ppv2.sh A B C D
A
B
C
D
4
#输出$1 至$4 的值并输出位置参数的个数

相关文章