[golang][译]使用os/exec执行命令
[golang][译]使用os/exec执行命令
https://colobu.com/2017/06/19/advanced-command-execution-in-Go-with-os-exec/
原文: Advanced command execution in Go with os/exec by Krzysztof Kowalczyk.
完整代码在作者的github上: advanced-exec
Go可以非常方便地执行外部程序,让我们开始探索之旅吧。
执行命令并获得输出结果
最简单的例子就是运行ls -lah
并获得组合在一起的stdout/stderr输出。
func main() {
cmd := exec.Command("ls", "-lah")
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
fmt.Printf("combined out:\n%s\n", string(out))
}
将stdout和stderr分别处理
和上面的例子类似,只不过将stdout和stderr分别处理。
func main() {
cmd := exec.Command("ls", "-lah")
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
fmt.Printf("out:\n%s\nerr:\n%s\n", outStr, errStr)
}
命令执行过程中获得输出
如果一个命令需要花费很长时间才能执行完呢?
除了能获得它的stdout/stderr,我们还希望在控制台显示命令执行的进度。
有点小复杂。
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
var out []byte
buf := make([]byte, , )
for {
n, err := r.Read(buf[:])
if n > {
d := buf[:n]
out = append(out, d...)
os.Stdout.Write(d)
}
if err != nil {
// Read returns io.EOF at the end of file, which is not an error for us
if err == io.EOF {
err = nil
}
return out, err
}
}
// never reached
panic(true)
return nil, nil
}
func main() {
cmd := exec.Command("ls", "-lah")
var stdout, stderr []byte
var errStdout, errStderr error
stdoutIn, _ := cmd.StdoutPipe()
stderrIn, _ := cmd.StderrPipe()
cmd.Start()
go func() {
stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn)
}()
go func() {
stderr, errStderr = copyAndCapture(os.Stderr, stderrIn)
}()
err := cmd.Wait()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
if errStdout != nil || errStderr != nil {
log.Fatalf("failed to capture stdout or stderr\n")
}
outStr, errStr := string(stdout), string(stderr)
fmt.Printf("\nout:\n%s\nerr:\n%s\n", outStr, errStr)
}
命令执行过程中获得输出2
上一个方案虽然工作,但是看起来copyAndCapture
好像重新实现了io.Copy
。由于Go的接口的功能,我们可以重用io.Copy
。
我们写一个CapturingPassThroughWriter
struct,它实现了io.Writer
接口。它会捕获所有的数据并写入到底层的io.Writer
。
// CapturingPassThroughWriter is a writer that remembers
// data written to it and passes it to w
type CapturingPassThroughWriter struct {
buf bytes.Buffer
w io.Writer
}
// NewCapturingPassThroughWriter creates new CapturingPassThroughWriter
func NewCapturingPassThroughWriter(w io.Writer) *CapturingPassThroughWriter {
return &CapturingPassThroughWriter{
w: w,
}
}
func (w *CapturingPassThroughWriter) Write(d []byte) (int, error) {
w.buf.Write(d)
return w.w.Write(d)
}
// Bytes returns bytes written to the writer
func (w *CapturingPassThroughWriter) Bytes() []byte {
return w.buf.Bytes()
}
func main() {
var errStdout, errStderr error
cmd := exec.Command("ls", "-lah")
stdoutIn, _ := cmd.StdoutPipe()
stderrIn, _ := cmd.StderrPipe()
stdout := NewCapturingPassThroughWriter(os.Stdout)
stderr := NewCapturingPassThroughWriter(os.Stderr)
err := cmd.Start()
if err != nil {
log.Fatalf("cmd.Start() failed with '%s'\n", err)
}
go func() {
_, errStdout = io.Copy(stdout, stdoutIn)
}()
go func() {
_, errStderr = io.Copy(stderr, stderrIn)
}()
err = cmd.Wait()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
if errStdout != nil || errStderr != nil {
log.Fatalf("failed to capture stdout or stderr\n")
}
outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
fmt.Printf("\nout:\n%s\nerr:\n%s\n", outStr, errStr)
}
命令执行过程中获得输出3
事实上Go标准库包含一个更通用的io.MultiWriter
,我们可以直接使用它。
func main() {
var stdoutBuf, stderrBuf bytes.Buffer
cmd := exec.Command("ls", "-lah")
stdoutIn, _ := cmd.StdoutPipe()
stderrIn, _ := cmd.StderrPipe()
var errStdout, errStderr error
stdout := io.MultiWriter(os.Stdout, &stdoutBuf)
stderr := io.MultiWriter(os.Stderr, &stderrBuf)
err := cmd.Start()
if err != nil {
log.Fatalf("cmd.Start() failed with '%s'\n", err)
}
go func() {
_, errStdout = io.Copy(stdout, stdoutIn)
}()
go func() {
_, errStderr = io.Copy(stderr, stderrIn)
}()
err = cmd.Wait()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
if errStdout != nil || errStderr != nil {
log.Fatal("failed to capture stdout or stderr\n")
}
outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes())
fmt.Printf("\nout:\n%s\nerr:\n%s\n", outStr, errStr)
}
改变执行程序的环境(environment)
你已经知道了怎么在程序中获得环境变量,对吧: `os.Environ()`返回所有的环境变量[]string
,每个字符串以FOO=bar
格式存在。FOO
是环境变量的名称,bar
是环境变量的值, 也就是os.Getenv("FOO")
的返回值。
有时候你可能想修改执行程序的环境。
你可设置exec.Cmd
的Env
的值,和os.Environ()
格式相同。通常你不会构造一个全新的环境,而是添加自己需要的环境变量:
cmd := exec.Command("programToExecute")
additionalEnv := "FOO=bar"
newEnv := append(os.Environ(), additionalEnv))
cmd.Env = newEnv
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
}
fmt.Printf("%s", out)
包 shurcooL/go/osutil提供了便利的方法设置环境变量。
预先检查程序是否存在
想象一下你写了一个程序需要花费很长时间执行,再最后你调用foo
做一些基本的任务。
如果foo
程序不存在,程序会执行失败。
当然如果我们预先能检查程序是否存在就完美了,如果不存在就打印错误信息。
你可以调用exec.LookPath
方法来检查:
func checkLsExists() {
path, err := exec.LookPath("ls")
if err != nil {
fmt.Printf("didn't find 'ls' executable\n")
} else {
fmt.Printf("'ls' executable is in '%s'\n", path)
}
}
另一个检查的办法就是让程序执行一个空操作, 比如传递参数"--help"显示帮助信息。
下面的章节是译者补充的内容
管道
我们可以使用管道将多个命令串联起来, 上一个命令的输出是下一个命令的输入。
使用os.Exec
有点麻烦,你可以使用下面的方法:
package main
import (
"bytes"
"io"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
r, w := io.Pipe()
c1.Stdout = w
c2.Stdin = r
var b2 bytes.Buffer
c2.Stdout = &b2
c1.Start()
c2.Start()
c1.Wait()
w.Close()
c2.Wait()
io.Copy(os.Stdout, &b2)
}
或者直接使用Cmd
的StdoutPipe
方法,而不是自己创建一个io.Pipe`。
package main
import (
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
_ = c1.Run()
_ = c2.Wait()
}
管道2
上面的解决方案是Go风格的解决方案,事实上你还可以用一个"Trick"来实现。
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := "cat /proc/cpuinfo | egrep '^model name' | uniq | awk '{print substr($0, index($0,$4))}'"
out, err := exec.Command("bash", "-c", cmd).Output()
if err != nil {
fmt.Printf("Failed to execute command: %s", cmd)
}
fmt.Println(string(out))
}
https://www.cnblogs.com/landv/p/11584253.html
[golang][译]使用os/exec执行命令的更多相关文章
- golang os/exec 执行外部命令
exec包执行外部命令,它将os.StartProcess进行包装使得它更容易映射到stdin和stdout,并且利用pipe连接i/o. func LookPath(file string) (st ...
- [go]os/exec执行shell命令
// exec基础使用 import ( "os/exec" ) cmd = exec.Command("C:\\cygwin64\\bin\\bash.exe" ...
- go os/exec执行外部程序
Go提供的os/exec包可以执行外部程序,比如调用系统命令等. 最简单的代码,调用pwd命令显示程序当前所在目录: package main import ( "fmt" &qu ...
- golang语言中os/exec包的学习与使用
package main; import ( "os/exec" "fmt" "io/ioutil" "bytes" ) ...
- pycharm下 os.system os.popen执行命令返回有中文乱码
原文 settings:
- golang执行命令行(一)
golang中会经常遇到要 fork 子进程的需求.go 标准库为我们封装了 os/exec标准包,当我们要运行外部命令时应该优先使用这个库. 执行 command 这里我简单结合context 和 ...
- Golang os/exec 实现
os/exec 实现了golang调用shell或者其他OS中已存在的命令的方法. 本文主要是阅读内部实现后的一些总结. 如果要运行ls -rlt,代码如下: package main import ...
- golang中os/exec包用法
exec包执行外部命令,它将os.StartProcess进行包装使得它更容易映射到stdin和stdout,并且利用pipe连接i/o. 1.func LookPath(file string) ( ...
- Docker exec 宿主对容器执行命令 - 五
Docker 下 exec 从宿主机对容器执行命名操作 docker exec -it # 交互可以进入容器 ; exec 也可以在宿主机对容器执行命令: docker attach # 也是可 ...
随机推荐
- 转:JVM的符号引用和直接引用
在JVM中类加载过程中,在解析阶段,Java虚拟机会把类的二级制数据中的符号引用替换为直接引用. 1.符号引用(Symbolic References): 符号引用以一组符号来描述所引用的目标,符号可 ...
- 1-redis使用笔记
1.清空当前redis数据库缓存FLUSHDB flushdb 2.清空整个redis缓存FLUSHALL flushall 3.设置 SET w3ckey redis 4.获取 GET w3ckey ...
- catch SocketException
https://stackoverflow.com/questions/32810051/cannot-catch-socketexception/32810079#32810079 https:// ...
- Markdown 初学总结
Markdown Tutorial(Typora-Specific) 1. Headers 最多可有六级标题,在标题前加 # 作为标记.注意标记与标题内容之间有空格: # 这是一级标题 ## 这是二级 ...
- C# 后台获取GridView列表的值
int rowIndex = ((GridViewRow)((Button)sender).NamingContainer).RowIndex;//获取gridview中的行号 ...
- django AJAX 的应用
目录 AJAX 的使用 AJAX简介 AJAX常见应用情景 AJAX的优缺点 jQuery实现的AJAX JS实现AJAX AJAX请求如何设置csrf_token Form表单上传文件 AJAX上传 ...
- Navicat中文破解版(windows10)
1.下载安装包 链接:https://pan.baidu.com/s/1hP3cD9aTv8nvJfNwjXPdrQ 提取码:xmsu 2.解压安装包 选择.exe 文件然后 安装 选择自己要安装的 ...
- spark 机器学习 随机森林 原理(一)
1.什么是随机森林顾名思义,是用随机的方式建立一个森林,森林里面有很多的决策树组成,随机森林的每一棵决 策树之间是没有关联的.在得到森林之后,当有一个新的输入样本进入的时候,就让森林中的每一棵决策树分 ...
- Android笔记(四十七) Android中的数据存储——XML(三)SAX解析
SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备. SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SA ...
- MySQL 空事务
问题描述; 研发同事反应MySQL数据库有锁,检查innodb_trx时,发现有很多长时间未结束的空事务. 这些事务的trx_mysql_thread_id都为0,因此不能通过kill ...