Skip to content

Commit 4b9af0e

Browse files
committed
docs: edit debug
1 parent a360d05 commit 4b9af0e

5 files changed

Lines changed: 291 additions & 150 deletions

File tree

docs/archives/error.md

Lines changed: 0 additions & 117 deletions
This file was deleted.

docs/debug.md

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# 脚本除错
2+
3+
本章介绍如何对 Shell 脚本除错。
4+
5+
## 常见错误
6+
7+
编写 Shell 脚本的时候,一定要考虑到命令失败的情况,否则很容易出错。
8+
9+
```bash
10+
#! /bin/bash
11+
12+
dir_name=/path/not/exist
13+
14+
cd $dir_name
15+
rm *
16+
```
17+
18+
上面脚本中,如果目录`$dir_name`不存在,`cd $dir_name`命令就会执行失败。这时,就不会改变当前目录,脚本会继续执行下去,导致`rm *`命令删光当前目录的文件。
19+
20+
如果改成下面的样子,也会有问题。
21+
22+
```bash
23+
cd $dir_name && rm *
24+
```
25+
26+
上面脚本中,只有`cd $dir_name`执行成功,才会执行`rm *`。但是,如果变量`$dir_name`为空,`cd`就会进入用户主目录,从而删光用户主目录的文件。
27+
28+
下面的写法才是正确的。
29+
30+
```bash
31+
[[ -d $dir_name ]] && cd $dir_name && rm *
32+
```
33+
34+
上面代码中,先判断目录`$dir_name`是否存在,然后才执行其他操作。
35+
36+
如果不放心删除什么文件,可以先打印出来看一下。
37+
38+
```bash
39+
[[ -d $dir_name ]] && cd $dir_name && echo rm *
40+
```
41+
42+
上面命令中,`echo rm *`不会删除文件,只会打印出来要删除的文件。
43+
44+
## `bash``-x`参数
45+
46+
`bash``-x`参数可以在执行每一行命令之前,打印该命令。这样就不用自己输出执行的命令,一旦出错,比较容易追查。
47+
48+
下面是一个脚本`script.sh`
49+
50+
```bash
51+
# script.sh
52+
echo hello world
53+
```
54+
55+
加上`-x`参数,执行每条命令之前,都会显示该命令。
56+
57+
```bash
58+
$ bash -x script.sh
59+
+ echo hello world
60+
hello world
61+
```
62+
63+
上面例子中,行首为`+`的行,显示该行是所要执行的命令,下一行才是该命令的执行结果。
64+
65+
下面再看一个`-x`写在脚本内部的例子。
66+
67+
```bash
68+
#! /bin/bash -x
69+
# trouble: script to demonstrate common errors
70+
71+
number=1
72+
if [ $number = 1 ]; then
73+
echo "Number is equal to 1."
74+
else
75+
echo "Number is not equal to 1."
76+
fi
77+
```
78+
79+
上面的脚本执行之后,会输出每一行命令。
80+
81+
```bash
82+
$ trouble
83+
+ number=1
84+
+ '[' 1 = 1 ']'
85+
+ echo 'Number is equal to 1.'
86+
Number is equal to 1.
87+
```
88+
89+
输出的命令之前的`+`号,是由系统变量`PS4`决定,可以修改这个变量。
90+
91+
```bash
92+
$ export PS4='$LINENO + '
93+
$ trouble
94+
5 + number=1
95+
7 + '[' 1 = 1 ']'
96+
8 + echo 'Number is equal to 1.'
97+
Number is equal to 1.
98+
```
99+
100+
另外,`set`命令也可以设置 Shell 的行为参数,有利于脚本除错,详见《set 命令》一章。
101+
102+
## 环境变量
103+
104+
有一些环境变量常用于除错。
105+
106+
### LINENO
107+
108+
变量`LINENO`返回它在脚本里面的行号。
109+
110+
```bash
111+
#!/bin/bash
112+
113+
echo "This is line $LINENO"
114+
```
115+
116+
执行上面的脚本`test.sh``$LINENO`会返回`3`
117+
118+
```bash
119+
$ ./test.sh
120+
This is line 3
121+
```
122+
123+
### FUNCNAME
124+
125+
变量`FUNCNAME`返回一个数组,内容是当前的函数调用堆栈。堆栈最底层的函数(最后一个调用的函数)是该数组的0号成员,以此类推。
126+
127+
```bash
128+
#!/bin/bash
129+
130+
function func1()
131+
{
132+
echo "func1: FUNCNAME0 is ${FUNCNAME[0]}"
133+
echo "func1: FUNCNAME1 is ${FUNCNAME[1]}"
134+
echo "func1: FUNCNAME2 is ${FUNCNAME[2]}"
135+
func2
136+
}
137+
138+
function func2()
139+
{
140+
echo "func2: FUNCNAME0 is ${FUNCNAME[0]}"
141+
echo "func2: FUNCNAME1 is ${FUNCNAME[1]}"
142+
echo "func2: FUNCNAME2 is ${FUNCNAME[2]}"
143+
}
144+
145+
func1
146+
```
147+
148+
执行上面的脚本`test.sh`,结果如下。
149+
150+
```bash
151+
$ ./test.sh
152+
func1: FUNCNAME0 is func1
153+
func1: FUNCNAME1 is main
154+
func1: FUNCNAME2 is
155+
func2: FUNCNAME0 is func2
156+
func2: FUNCNAME1 is func1
157+
func2: FUNCNAME2 is main
158+
```
159+
160+
上面例子中,执行`func1`时,变量`FUNCNAME`的0号成员是`func1`,1号成员是调用`func1`的主脚本`main`。执行`func2`时,变量`FUNCNAME`的0号成员是`func2`,1号成员是调用`func2``func1`
161+
162+
### BASH_SOURCE
163+
164+
变量`BASH_SOURCE`返回一个数组,内容是当前的脚本调用堆栈。堆栈最底层的脚本(即当前执行的脚本)是该数组的0号成员,以此类推。
165+
166+
下面有两个子脚本`lib1.sh``lib2.sh`
167+
168+
```bash
169+
# lib1.sh
170+
function func1()
171+
{
172+
echo "func1: BASH_SOURCE0 is ${BASH_SOURCE[0]}"
173+
echo "func1: BASH_SOURCE1 is ${BASH_SOURCE[1]}"
174+
echo "func1: BASH_SOURCE2 is ${BASH_SOURCE[2]}"
175+
func2
176+
}
177+
```
178+
179+
```bash
180+
# lib2.sh
181+
function func2()
182+
{
183+
echo "func2: BASH_SOURCE0 is ${BASH_SOURCE[0]}"
184+
echo "func2: BASH_SOURCE1 is ${BASH_SOURCE[1]}"
185+
echo "func2: BASH_SOURCE2 is ${BASH_SOURCE[2]}"
186+
}
187+
```
188+
189+
然后,主脚本`main.sh`调用上面两个子脚本。
190+
191+
```bash
192+
#!/bin/bash
193+
# main.sh
194+
195+
source lib1.sh
196+
source lib2.sh
197+
198+
func1
199+
```
200+
201+
执行主脚本`main.sh`,会得到下面的结果。
202+
203+
```bash
204+
$ ./main.sh
205+
func1: BASH_SOURCE0 is lib1.sh
206+
func1: BASH_SOURCE1 is ./main.sh
207+
func1: BASH_SOURCE2 is
208+
func2: BASH_SOURCE0 is lib2.sh
209+
func2: BASH_SOURCE1 is lib1.sh
210+
func2: BASH_SOURCE2 is ./main.sh
211+
```
212+
213+
上面例子中,执行函数`func1`时,变量`BASH_SOURCE`的0号成员是`func1`所在的脚本`lib1.sh`,1号成员是主脚本`main.sh`;执行函数`func2`时,变量`BASH_SOURCE`的0号成员是`func2`所在的脚本`lib2.sh`,1号成员是调用`func2`的脚本`lib1.sh`
214+
215+
### BASH_LINENO
216+
217+
变量`BASH_SOURCE`返回一个数组,内容是每一轮调用堆栈对应的行号。`${BASH_LINENO[$i]}``${FUNCNAME[$i+1]}`在文件`${BASH_SOURCE[$i+1]}`里面被调用时的行号。
218+
219+
下面有两个子脚本`lib1.sh``lib2.sh`
220+
221+
```bash
222+
# lib1.sh
223+
function func1()
224+
{
225+
echo "func1: BASH_LINENO is ${BASH_LINENO[0]}"
226+
echo "func1: FUNCNAME is ${FUNCNAME[1]}"
227+
echo "func1: BASH_SOURCE is ${BASH_SOURCE[1]}"
228+
229+
func2
230+
}
231+
```
232+
233+
```bash
234+
# lib2.sh
235+
function func2()
236+
{
237+
echo "func2: BASH_LINENO is ${BASH_SOURCE[0]}"
238+
echo "func2: FUNCNAME is ${FUNCNAME[1]}"
239+
echo "func2: BASH_SOURCE is ${BASH_SOURCE[1]}"
240+
}
241+
```
242+
243+
然后,主脚本`main.sh`调用上面两个子脚本。
244+
245+
```bash
246+
#!/bin/bash
247+
# main.sh
248+
249+
source lib1.sh
250+
source lib2.sh
251+
252+
func1
253+
```
254+
255+
执行主脚本`main.sh`,会得到下面的结果。
256+
257+
```bash
258+
$ ./main.sh
259+
func1: BASH_LINENO is 7
260+
func1: FUNCNAME is main
261+
func1: BASH_SOURCE is main.sh
262+
func2: BASH_LINENO is 8
263+
func2: FUNCNAME is func1
264+
func2: BASH_SOURCE is lib1.sh
265+
```
266+
267+
上面例子中,对于函数`func1`来说,它是在`main.sh`的第7行调用的;对于函数`func2`来说,它是在`lib1.sh`的第8行调用的。
268+

docs/mktemp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ TMPFILE=$(mktemp) || exit 1
7575
echo "Our temp file is $TMPFILE"
7676
```
7777

78-
## 四、mktemp 命令的参数
78+
## mktemp 命令的参数
7979

8080
`-d`参数可以创建一个临时目录。
8181

0 commit comments

Comments
 (0)