bash新手入门指南-预设特殊的变量
在bash
中,提供了一些特殊的预定义变量,也可以说是预设的特殊参数,可以直接引用使用,极大提升了脚本的灵活性和表达能力。
特殊参数
在bash
中,有如下特殊参数,这些参数只能被引用(使用$
进行引用),无法再次被赋值(语法不允许),具体特殊参数如下:
*
: 以一个字符串来表示所有参数@
:表示所有的参数,但每个参数独立#
:参数个数?
:上一条命令的退出状态码$
:表示当前shell
的pid
!
:表示上一个放入后台执行任务的pid
0
:表示脚本名称n
:数字n
表示第几个参数
表示参数的个数
在bash
中,调用shell
的时候,是允许向脚本传入参数的,在脚本中可以使用$#
来获取参数的个数,比如:
bash
#!/bin/bash
echo $#
上述脚本直接输出$#
变量的值,即传入的参数,运行脚本的时候,添加相应的参数,可以获取到结果:
bash
# bash var_declaration.sh 1 2 3
3
#
参数位置个数是由shell
调用命令行传入的参数来决定的,默认情况下,以空格进行划分,如有多个空格,也不会影响结果,若传入的数据有空格,则需要使用单引号或者双引号进行括起来调用。例如:
bash
# bash var_declaration.sh '1 2' "3 4" 5 6
4
#
获取具体的参数数据
bash
echo 1: $1
echo 2: $2
echo 5: $5
执行后的结果为:
bash
# bash var_declaration.sh 1 2 3 4
1: 1
2: 2
5:
#
当超过9
以后,也可以通过$n
来获取,比如$65534
即获取第65534
个参数,不过为了方便查看,建议位置参数也使用{}
括起来,比如下面2条语句,含义都是一样的:
bash
echo 1: ${1}
echo 1: $1
其中若n
为0
的话,则代表脚本本身,比如:
bash
echo script_name: ${0}
运行后结果为:
bash
# bash var_declaration.sh
script_name: var_declaration.sh
#
获取脚本或命令的pid
在bash
中,使用$$
可以获取当前脚本的pid
,使用$!
可以获取上一个被放入后台的命令pid
。
使用$$
获取脚本的pid
。
bash
echo script_pid: $$
运行后结果:
bash
# bash var_declaration.sh
script_pid: 26209
#
获取上一个被放入后台的pid
则使用$!
即可获取,在bash
中,命令/脚本被放入后台,只需要在最后添加一个&
即可,放入后台后的脚本,不会阻塞后续的运行,比如:
bash
echo 1
sleep 3 &
echo 2
其中sleep 3
将会被放入到后台执行,不会阻塞echo 2
的执行。
这里还有一个小知识,若脚本中有命令是后台执行的,当主脚本运行完毕后,这些后台命令如果还未被执行完毕,是不会被销毁的,而是被系统进程init
或者systemd
所接管,比如有如下脚本:
bash
#!/bin/bash
echo 1
sleep 1000 &
echo 2
sleep 20
如上脚本运行后,执行sleep 1000
且放入后台后,这个时候sleep 1000
这个进程的父进程是该脚本,当sleep 20
运行完毕后,该脚本销毁了,但是sleep 1000
还在运行中,这个时候就会被系统进程init
所接管,也就是所谓的pid
为1的进程所接管。
获取上一条命令退出状态码
在bash
中,可以使用$?
来获取上一条命令的退出状态码,和其他编程语言不通,bash
退出状态码,0
表示是正常的。而非0
表示是异常的。
一般而言,在脚本中,可以轻松判断上一个命令是否执行完毕,例如:
bash
#!/bin/bash
tar zcvf xxx.tar.gz xx
echo $?
如上脚本中的$?
是在打印其上一条语句tar
的执行结果,请注意,$?
是判断其上一条命令的执行结果,如果有间隔,则无法判别,比如:
bash
tar zcvf xxx.tar.gz xx
echo "123"
echo $?
这个时候,其$?
是在判断echo "123"
的执行结果,而非tar
的命令结果。
获取所有的参数
在bash
中,可以通过$*
和$@
来获取所有的命令行参数,其用法如下:
bash
#!/bin/bash
echo '$@'
echo $@
echo '$*'
echo $*
执行结果如下:
bash
# bash var_declaration.sh 1 2 3 '4 5'
$@
1 2 3 4 5
$*
1 2 3 4 5
#
乍一看, <math xmlns="http://www.w3.org/1998/Math/MathML"> @ 和 @ 和 </math>@和* 的效果似乎相同,但它们的主要区别在于是否被引号包裹时的行为的不同。这里为了更清晰的表达这一点,下面使用了2个脚本示例通过 for 循环分别对这两个变量进行迭代,脚本如下:
bash
#!/bin/bash
for i in "$@"
do
echo i: $i
done
使用 "$@" 时,输出的内容还是会按照脚本的传入参数进行分割处理。具体结果如下所示:
bash
# bash var_declaration.sh 1 2 3
i: 1
i: 2
i: 3
#
如果将其改为 "$*"
, 那么脚本会将传入的所有参数将被视为一个整体的字符串来进行处理。示例如下:
bash
#!/bin/bash
for i in "$*"
do
echo i: $i
done
其执行结果为:
bash
# bash var_declaration.sh 1 2 3
i: 1 2 3
#
所以,可以做一个简单的总结:使用 "$@"
时,参数依然会逐个被分割处理;而使用 "$*"
时,所有参数则会作为一个整体的字符串进行处理。
如果传入的参数中包含有引号作为整体传入的,"$@"
会将引号中的数据作为一个整体传入,例如有脚本如下:
bash
#!/bin/bash
for i in "$@"
do
echo ii: ${i}
done
当传入1 2 3 "4 5 6" '7 8' '"9 10" "11 12"'
参数的时候,会将"4 5 6"
、'7 8'
、`'"9 10" "11 12"'``都当做一个整体,其结果如下:
bash
# bash var_declaration.sh 1 2 3 "4 5 6" '7 8' '"9 10" "11 12"'
ii: 1
ii: 2
ii: 3
ii: 4 5 6
ii: 7 8
ii: "9 10" "11 12"
#
所以总结一下规律:
当使用 $@
和 $*
时,二者的行为基本一致,都会将所有位置参数展开。
当使用 "$*"
时,所有参数会作为一个整体的字符串被传入。
当使用 "$@"
时,如果参数被引号包括的,会保证其整体性,一并被引入。
参数 | 含义 |
---|---|
$* | 如果命令行参数有部分是用引号括起来的,会将引号中的数据继续拆分。 |
$@ | 如果命令行参数有部分是用引号括起来的,会将引号中的数据继续拆分。 |
"$*" | 将所有命令行参数作为一个参数提供 |
"$@" | 如果命令行参数有引号括起来,会将括起来的当做一个整体来处理 |
总结
特殊参数是由bash
提前定义好的,只需要调用即可,都不是特别复杂,只是其中$*
和$@
容易弄混,这里可以简单描述一下用法,如果传入的参数中有空格,并且被引号括起来了的,那么使用"$@"
可以将引号中的内容单独引入,当做一个整体来看待。