[exec] 命令执行封装
本模块是一个高度封装的系统命令执行模块,支持基础的命令执行,同时也支持复杂的针对命令执行中内容的分析,本教程简单通过两个例子来介绍 exec
对 Golang 原生命令执行的封装。
exec.Command/CommandContext
)#
基础 API:类原生:这两个 API 是最简单也是最容易理解的
fn exec.Command(var_1: string): (*exec.Cmd, error)
输入一个想执行的命令,返回一个 Golang 原生的*exec.Cmd
对象fn exec.CommandContext(var_1: context.Context, var_2: string): (*exec.Cmd, error)
输入一个想执行的命令,返回一个 Golang 原生的*exec.Cmd
对象,支持 Context 控制生命周期
我们列出 *exec.Cmd
可用的操作与字段,如有需要可以自行取用。
type os/exec.(Cmd) struct { Fields(可用字段): Path: string Args: []string Env: []string Dir: string Stdin: io.Reader Stdout: io.Writer Stderr: io.Writer ExtraFiles: []*os.File SysProcAttr: *syscall.SysProcAttr Process: *os.Process ProcessState: *os.ProcessState StructMethods(结构方法/函数): PtrStructMethods(指针结构方法/函数): func CombinedOutput() return([]uint8, error) func Output() return([]uint8, error) func Run() return(error) func Start() return(error) func StderrPipe() return(io.ReadCloser, error) func StdinPipe() return(io.WriteCloser, error) func StdoutPipe() return(io.ReadCloser, error) func String() return(string) func Wait() return(error) }
exec.System/SystemContext
#
简化 API:这两个 API 是最容易让人理解的,基本同 python
中 os.system()
,可以执行一条系统命令,然后把结果返回回来(bytes, error)
fn exec.System(var_1: string): ([]uint8, error)
执行一条系统命令,返回 bytes 与 errorfn exec.SystemContext(var_1: context.Context, var_2: string): ([]uint8, error)
,执行一条系统命令(context 控制生命周期),返回 bytes 与 error
使用案例如下
raw, err = exec.System("ls")die(err)dump(raw)
当我们执行上面例子的时候,本质上是执行了一条系统命令 ls
([]uint8) (len=32 cap=1536) { 00000000 53 32 5f 30 34 36 2e 79 61 6b 0a 61 2e 79 61 6b |S2_046.yak.a.yak| 00000010 0a 62 2e 79 61 6b 0a 74 65 73 74 2e 79 61 6b 0a |.b.yak.test.yak.|}
exec.SystemBatch
#
多命令批量执行:fn exec.SystemBatch(var_1: string, vars: ...yaklib.poolOpt)
这个函数是 yak 的特有功能,fuzz.Strings
这个函数大家如果有有了解的话,其实就很容易理解这个批量系统命令执行的能力了。
通过 fuzz.Strings
,拆成多个字符串(命令),以一定并发批量执行所有的命令,执行的结果通过 exec.callback
参数返回结果交给用户处理。
经典案例如下:
exec.SystemBatch(`echo {{net:(192.168.1.1/28,example.com)}}` , exec.callback(func(cmd, results){ println(`exec: `, `results: `, codec.EncodeASCII(string(results)))}))
执行结果如下:
exec: results: "192.168.1.7\n"exec: results: "192.168.1.6\n"exec: results: "192.168.1.3\n"exec: results: "192.168.1.0\n"exec: results: "192.168.1.5\n"exec: results: "192.168.1.11\n"exec: results: "192.168.1.10\n"exec: results: "192.168.1.8\n"exec: results: "192.168.1.15\n"exec: results: "example.com\n"exec: results: "192.168.1.14\n"exec: results: "192.168.1.9\n"exec: results: "192.168.1.2\n"exec: results: "192.168.1.13\n"exec: results: "192.168.1.12\n"exec: results: "192.168.1.4\n"exec: results: "192.168.1.1\n"
大家看到结果就肯定知道发生了什么,我们的 {{net()}}
标签将标签内的参数作为网段和域名拆解,并把结果返回到 echo 中。
执行的结果通过 exec.callback(func(cmd, results){ // do sth })
这个参数回传给了用户。
当然,我们还有其他参数可以设置
#
SystemBatch 的可用参数fn exec.callback(var_1: func(string, []uint8)): yaklib.poolOpt
设置回调函数(最常用)fn exec.concurrent(var_1: int): yaklib.poolOpt
命令执行并发量,同时支持多少个命令同时执行?fn exec.timeout(var_1: float64): yaklib.poolOpt
超时设置
exec.WatchOutput/WatchStdout/WatchStderr
#
监控特性:这三个命令其实是非常棒的封装,当我们调用系统命令的时候,有时经常需要中间结果。
如果命令行工具并不支持将中间结果以某些合理的形式输出的话,我们通常需要等到命令结束,拿到结果,进行处理。
我们的 exec.WatchOutput
很好的解决了这个问题:
监控一个命令执行的中间结果,一般用于检测这个命令是否得到了想要的结果,或者获取一个命令的中间结果。该函数监控命令执行的标准输出流+标准错误流结果
该命令在执行过程中,会把结果每秒输出一次,通过回调函数 func callback(results: bytes) bool 来输出结果,
fn exec.WatchOutput(system: string, seconds: float64, callback: func([]uint8) bool): error
我们以下面例子作为展示
exec.WatchOutput(`ping 8.8.8.8`, 4, def callback(result) { println(now()) println(string(result)) return true})
上面意思是,我们执行 ping 8.8.8.8
最多执行 4 秒钟,每一秒钟返回一次标准错误+标准输出,把时间戳和返回的内容打印出来。
danger
注意上述 callback
返回值为 bool,如果返回值为 true,表示继续执行,如果返回值为 false,会立即停止这个命令的执行。
执行上述结果为:
2021-06-28 21:25:28.298526 +0800 CST m=+1.030134668PING 8.8.8.8 (8.8.8.8): 56 data bytes64 bytes from 8.8.8.8: icmp_seq=0 ttl=113 time=108.981 ms
2021-06-28 21:25:29.29681 +0800 CST m=+2.028436126PING 8.8.8.8 (8.8.8.8): 56 data bytes64 bytes from 8.8.8.8: icmp_seq=0 ttl=113 time=108.981 ms64 bytes from 8.8.8.8: icmp_seq=1 ttl=113 time=161.165 ms
2021-06-28 21:25:30.297573 +0800 CST m=+3.029216584PING 8.8.8.8 (8.8.8.8): 56 data bytes64 bytes from 8.8.8.8: icmp_seq=0 ttl=113 time=108.981 ms64 bytes from 8.8.8.8: icmp_seq=1 ttl=113 time=161.165 ms64 bytes from 8.8.8.8: icmp_seq=2 ttl=113 time=98.257 ms
2021-06-28 21:25:31.29788 +0800 CST m=+4.029542168PING 8.8.8.8 (8.8.8.8): 56 data bytes64 bytes from 8.8.8.8: icmp_seq=0 ttl=113 time=108.981 ms64 bytes from 8.8.8.8: icmp_seq=1 ttl=113 time=161.165 ms64 bytes from 8.8.8.8: icmp_seq=2 ttl=113 time=98.257 ms64 bytes from 8.8.8.8: icmp_seq=3 ttl=113 time=117.291 ms
同理,exec.WatchStderr/WatchStdout
使用方法完全相同,只不过针对的输出监控不一样,一个是只监控标准错误流,一个是只监控标准输出流。