-
Notifications
You must be signed in to change notification settings - Fork 0
/
第 12.00.01.01 章、os_exec 包
300 lines (207 loc) · 9.08 KB
/
第 12.00.01.01 章、os_exec 包
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
总览
exec 包运行外部命令
它包裹了 os.StartProcess 命令,让它更容易重映射 stdin 和 stdout,用管道连接 I/O,以及执行其他的调整
与 C 和其他语言所调用的 “system” 库不同,
os/exec 包有意不调用 系统 shell,也不会扩展 glob 样式,或者处理 shell 本会处理的其他的扩展/管线/重定向等
这个包更像 C 的 “exec” 函数族
若要扩展 glob 样式,可以直接调用 shell,来转义任何危险的输入,或者使用 path/filepath 包的 Glob 函数
若要扩展环境变量,使用 os 包的 ExpandEnv 函数
注意本包的样例假设为 Unix 系统
它们并不会在 Windows 上运行,它们也不会在 Go Playground 上运行
变量
若一个路径查找未能找到一个可执行文件时,产生一个 ErrNotFound 错误
var ErrNotFound = errors.New("executable file not found in $PATH")
LookPath(file string) (string, error)
从环境变量的 $PATH 中查找指定可执行文件的路径
若 file 包含一个斜线,则直接从测试它,而不查询 PATH
结果可能是绝对路径或者相对当前目录的路径
Cmd struct 类型
Cmd 表示了一个外部程序正被准备或正被执行
一个 Cmd 在调用其 Run/Output/CombinedOutput 方法后便不能再次使用
type Cmd struct {
// Path 是将要运行的命令的路径
//
// 这是仅有的必须被设为非零值的值
// 若 Path 是相对路径,则它被解算为相对 Dir 的路径
Path string
// Args 持有命令行参数,包含作为 Args[0] 存在的命令本身
// 若 Args 字段为空或 nil,则 Run 使用 {Path}
//
// 在典型的用法中, Path 和 Args 均在调用 Command 方法时被设置
Args []string
// Env 指定了进程的环境
// 每个条目都以 "key=value" 的形式出现
// 若 Env 是 nil,新进程使用当前进程的环境
// 若 Env 中包含重复的环境键,仅有 slice 中,每个重复键的最后一个值被使用
Env []string
// Dir 指定了命令的工作路径
// 若 Dir 为空字符串, Run 方法会在调用者进程的当前目录下执行
Dir string
// Stdin 指定了进程的标准输入
// 若 Stdin 是 nil,则进程从 空设备(os.DevNull)中读取
// 若 Stdin 是一个 *os.File,则进程的标准输入直接连接至那个文件
// 其他情况下,在命令执行的过程中,
// 一个独立的 goroutine 从 Stdin 读取数据,接着将数据通过管道运送至命令
// 在这种情况下, Wait 方法不会完成
// 直到 goroutine 停止拷贝
// 或者其读到了 goroutine 的末尾(EOF 或读取错误)
// 或者由于 写入管道返回了一个错误
Stdin io.Reader
// Stdout 和 Stderr 指定了进程的标准输出和标准错误
//
// 若两者的任何一个为 nil,则 Run 方法将对应的文件描述符连接至空设备
//
// 若 Stdout 和 Stderr 是同一个 writter,也具有一个可以用 == 来比较的类型
// 则任意时刻,最多有一个 goroutine 将调用 Write 方法
Stdout io.Writer
Stderr io.Writer
// ExtraFiles 指定了新进程会继承的附加的打开文件
// 它并不包含 标准输入/标准输出/标准错误
// 若非 nil,条目 i 会变成文件描述符 3+i
ExtraFile []*os.File
// SysProcAttr 持有可选的,操作系统特定的属性
// Run 方法将它作为 os.ProcAttr 的 Sys 字段 传入 os.StartProcess 中
Process *os.Process
// 包含过滤的或未导出的字段
}
Command(name string, arg ...string) *Cmd
返回一个 Cmd 结构体,来执行命名的程序以及其附带的参数
它仅设置返回的结构体的 Path 和 Args 字段
若 name 不含路径分隔符,则 Command 使用 LookPath 来解析名称以获得可能的完整路径
其他情况,它会直接使用 Path
返回的 Cmd 的 Args 字段由 命令名 以及跟随的 参数 构成
所以 arg 并不应该包含命令名自身
举例来说, Command("echo", "hello") 的 Args[0] 永远是 name,并非解析出来的 Path
CommandContext(ctx context.Context, name string, arg ...string) *Cmd
本函数与 Command 类似,但是包含一个 context
提供的 context 用于杀死进程(以调用 os.Process.Kill 的方法),若在命令自主结束之前,context 就变为完成状态
EG.
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil {
// 在 100 毫秒之后失败。
// 5 秒的 sleep 命令将被打断
}
(c *Cmd) CombinedOutput() ([]byte, error)
运行命令并返回混合的标准输出和标准错误
EG.
cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
stdoutStderr, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", stdoutStderr)
(c *Cmd) Output() ([]byte, error)
运行命令并返回其标准输出
任何返回的错误通常为 *ExitError 类型
如果 c.Stderr 是 nil,本方法产生 ExitError.Stderr
(c *Cmd) Run() error
启动指定的命令并等待其完成
当命令运行,且拷贝到 stdin stdout stderr 没有问题,且程序以状态号 0 返回, error 才是 0
若命令启动但未成功结束,则错误类型为 *ExitError
其它类型的错误会应为其它情况而返回
EG.
cmd := exec.Command("sleep", "1")
log.Printf("Running command and waiting for it to finish...")
err := cmd.Run()
log.Printf("Command finished with error: %v", err)
(c *Cmd) Start() error
启动一个特定的进程但不等待其完成
一旦命令退出,Wait 方法会返回退出码和与之相关的资源
(c *Cmd) StderrPipe() (io.ReadCloser, error)
返回一个管道,在命令开始时,会连接上命令的标准错误
Wait 方法会在命令结束之后关闭管道,所以大多数调用者就不语要手动关闭它们了
但是,一个影响就是,在完成从管道中读取所有数据之前就调用 Wait 将会时不正确的
基于同样的道理,当使用 StderrPipe 时,使用 Run 就是不正确的
参见 StdoutPipe 方法的案例来了解常规用法
EG.
cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
stderr, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
slurp, _ := ioutil.ReadAll(stderr)
fmt.Printf("%s\n", slurp)
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
(c *Cmd) StdinPipe() (io.WriterClose, error)
返回一个管道,在命令开始时,会连接上命令的标准输入
Wait 方法会在命令结束之后关闭管道
调用者仅需在需要提前关闭管道的情况下调用管道的 Close 方法
举例来说,若命令在标准输入关闭前不会退出,则调用者必须关闭管道
EG.
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
go func() {
defer stdin.Close()
io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
}()
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
(c *Cmd) StdoutPipe() (io.ReadCloser, error)
返回一个管道,在命令开始时,会连接上命令的标准输出
Wait 方法会在命令结束之后关闭管道
调用者仅需在需要提前关闭管道的情况下调用管道的 Close 方法
举例来说,若命令在标准输入关闭前不会退出,则调用者必须关闭管道
EG.
cmd := exec.Command("echo", "-n", `{"Name": "Bob", "Age": 32}`)
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
var person struct {
Name string
Age int
}
if err := json.NewDecoder(stdout).Decode(&person); err != nil {
log.Fatal(err)
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d years old\n", person.Name, person.Age)
(c *Cmd) Wait() error
等待命令结束并等待任何拷贝至 stdout 或 stderr 的过程完成
命令必须已经被 Start 方法开始
若命令运行,在拷贝 stdin stdout stderr 时未产生错误,且退出号为 0 时,错误才为 nil
若命令运行但未能成功完成,则错误类型为 *ExitError
其它的错误类型可能会由于 I/O 问题而返回
若 c.Stdin 并非 *os.File,本方法同样会等待从 c.Stdin 拷贝至进程的标准输入的 I/O 循环拷贝的结束
Wait 会释放任何与 Cmd 相关的资源
Error struct 类型
本类型记录了一个执行失败的二进制文件的名称,以及其失败的原因
type Error struct {
Name string
Err error
}
(e *Error) Error() string
满足了 error 内建接口类型
ExitError struct 类型
一个 ExitError 报告了一个命令的非成功退出
type ExitError strcut {
*op.ProcessState
// 若标准错误并未被收集
// 则 Stderr 字段会持有从 Cmd.Output 方法出书的标准错误的子集
//
// 若错误输出过长, Stderr 字段可能仅包含一个输出的头部和尾部
// 以及代表中部被省略内容的字节的数量
//
// Stderr 字段用于 debug,由于包含错误信息
// 有其它需求的用户应该根据需要重定向 Cmd.Stderr
Stderr []byte
}
(e *ExitError) Error() string
满足了 error 内建接口类型