Linux指令之set指令

igxiaoshan Lv5

常用set命令属性

set -o 查看所有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
[root@CentOs7_001 ~]# set -o
allexport off
braceexpand on
emacs on
errexit off
errtrace off
functrace off
hashall on
histexpand on
history on
ignoreeof off
interactive-comments on
keyword off
monitor on
noclobber off
noexec off
noglob off
nolog off
notify off
nounset off
onecmd off
physical off
pipefail off
posix off
privileged off
verbose off
vi off
xtrace off

## 解析
-a:标示已修改的变量,以供输出至环境变量。
-b:使被中止的后台程序立刻回报执行状态。
-C:转向所产生的文件无法覆盖已存在的文件。
-d:Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。
-e:若指令传回值不等于0,则立即退出shell。
-f:取消使用通配符。
-h:自动记录函数的所在位置。
-H Shell:可利用"!"加<指令编号>的方式来执行history中记录的指令。
-k:指令所给的参数都会被视为此指令的环境变量。
-l:记录for循环的变量名称。
-m:使用监视模式。
-n:只读取指令,而不实际执行。
-p:启动优先顺序模式。
-P:启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。
-t:执行完随后的指令,即退出shell。
-u:当执行时使用到未定义过的变量,则显示错误信息。
-v:显示shell所读取的输入值。
-x:执行指令后,会先显示该指令及所下的参数。

set -e

说明

1
2
"Exit immediately if a simple command exits with a non-zero status."
也就是说,在"set -e"之后出现的代码,一旦出现了返回值非零,整个脚本就会立即退出。

示例

1
2
3
4
5
6
7
#! /bin/bash

set -e
ls a.txt | echo "hi" >/dev/null
echo $?
echo "test error"

结果

1
2
3
4
5
6
7
8


[root@CentOs7_001 test-shell]# bash test01

ls: cannot access a.txt: No such file or directory
0
test error

结论

没有a.txt文件,但是ls a.txt | echo “hi” >/dev/null的返回码是0,因为set -e只捕获最后一个管道命令的返回值,从而导致遇错不退出脚本,依然执行后面的echo “test”

set -o pipefail

说明

1
2
3
"If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a  non-zero  status,or zero if all commands in the pipeline exit successfully.  This option is disabled by default."

设置了这个选项以后,包含管道命令的语句的返回值,会变成最后一个返回非零的管道命令的返回值

示例

1
2
3
4
5
6
7
#!/bin/bash

set -o pipefail
ls a.txt | echo "hi" >/dev/null
[[ $? -ne 0 ]] && exit 1
echo "test"

结果

1
2
3
4
5

[root@CentOs7_001 test-shell]# bash test02

ls: cannot access a.txt: No such file or directory

结论

set -o pipefail是去捕获最后一个返回非零的管道命令的返回值,也就是ls a.txt的返回值;
没有a.txt文件,返回值为非0,因此[[ $? -ne 0 ]]条件成立,脚本退出,不执行echo “test”

set +h

hashall可以让Bash记录执行过得命令路径,并保存到一个内存的hash表中,当再次执行相同命令时就不需要再通过PATH变量查找该命令的路径,这样可以提高效率。

但有时,程序的路径发生了变化,因为有Hash记录的存在反而会导致命令执行失败

1
2
3
4
5
6
7
8
9
[root@localhost ~]# hash
命中 命令
7 /usr/bin/openssl
1 /usr/bin/date
1 /usr/bin/tr
1 /usr/bin/uuidgen
1 /usr/bin/strings
4 /usr/bin/md5sum

如果将md5sum命令移动到/usr/local/bin/目录,此时系统依然根据Hash表记录的位置执行命令,就无法找到md5sum命令

1
2
3
4
[root@localhost ~]# mv /usr/bin/md5sum /usr/local/bin/
[root@localhost ~]# md5sum
-bash: /usr/bin/md5sum: 没有那个文件或目录

可以使用hash -d删除某个记录信息,或者使用hash -r清空整个Hash表,再或者使用set+h禁用hash表来解决这个问题

1
2
3
4
5
6
7
8
[root@localhost ~]# set +h		#禁用hash表
[root@localhost ~]# hash
-bash: hash: 哈希已禁用
[root@localhost ~]# md5sum /etc/passwd #命令可以正常使用
6322f6fcbc6e157963f5aa74f9e35039 /etc/passwd


[root@localhost ~]# set -h #启用hash表

set -u

开启nounset属性可以有效防止变量未定义的错误

1
2
3
4
5
#!/bin/bash

set -u
useradd $1

执行结果: 没有给$1赋值,会报错

1
2
3
4

[root@CentOs7_001 test-shell]# bash test03
test03: line 4: $1: unbound variable

set -x

打开脚本调试功能,也可以通过bash -x ***.sh打开

如果输出结果以双加号(++)开头则表示命令在子Shell中执行

set -n

检查shell语法,但不会执行,等同于 bash -n xxx.sh