
有时候我们需要通过代码的方式去执行 linux 命令,那么 os/exec 这个系统库刚好提供了相应的功能。
Golang语言中提供了一个 os/exec 包,它提供了一组函数和结构,用于调用外部程序,这些外部程序可以是系统
自带的,也可以是用户自定义的。os/exec 包中提供了一组函数,用于执行系统命令,我们可以使用它来执行系
统的cmd命令行。
exec包执行外部命令,它将 os.StartProcess 进行包装使得它更容易映射到 stdin 和 stdout,并且利用 pipe 连接i/o。
参考文档:https://pkg.go.dev/os/exec
func Command(name string, arg ...string) *Cmd {}使用 exec.Command 函数来创建一个 Cmd 结构体,该函数接受两个参数,第一个参数是要执行的命令,第二个
参数是命令行参数,比如 ls -l,那么第一个参数就是 ls,第二个参数就是 -l。
func (c *Cmd) Run() error {}使用 Run 函数来执行这个命令,Run 函数会根据我们传入的参数来执行命令,并返回一个 error 类型的结果。
func main() {
var outbuf, errbuf bytes.Buffer
cmd := exec.Command("bash", "test.sh")
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
var err error = cmd.Run();
if err != nil {
log.Fatal(err)
}
fmt.Printf("Stdout: [%s]\n", string(outbuf.Bytes()))
fmt.Printf("Stderr: [%s]\n", string(errbuf.Bytes()))
}func main() {
cmd := exec.Command("bash", "test.sh")
var err error = cmd.Run();
if err != nil {
log.Fatal(err)
}
// 获取命令的退出状态码
exitCode := cmd.ProcessState.ExitCode()
fmt.Println("ExitCode:", exitCode)
}func main() {
f, err := exec.LookPath("ls")
if err != nil {
fmt.Println(err)
}
// /usr/bin/ls
fmt.Println(f)
}func main() {
// 进行字符串的替换
cmd := exec.Command("tr", "a-z", "A-Z")
cmd.Stdin = strings.NewReader("some input")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
// in all caps: SOME INPUT
fmt.Printf("in all caps: %s\n", out.String())
}//运行命令并返回其标准输出
func (c *Cmd) Output() ([]byte, error)
// 运行命令,并返回标准输出和标准错误
func (c *Cmd) CombinedOutput() ([]byte, error)注意:Output() 和 CombinedOutput() 不能够同时使用,因为 command 的标准输出只能有一个,同时使用的话,便会报错。
func main() {
cmd := exec.Command("bash", "test.sh")
out, err := cmd.Output()
if err != nil {
fmt.Println(err)
}
fmt.Printf("stdout=[%s]\n", string(out))
}func main() {
cmd := exec.Command("ls", "-l", "/opt/software/")
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(out))
}// 开始指定命令并且等待它执行结束,如果命令能够成功执行完毕,则返回nil,否则的话边会产生错误
func (c *Cmd) Run() error
// 使某个命令开始执行,但是并不等到他执行结束,这点和Run命令有区别,然后使用Wait方法等待命令执行完毕并且释放响应的资源
func (c *Cmd) Start() error 注:一个 command 只能使用 Start() 或者 Run() 中的一个启动命令,不能两个同时使用。
Start 执行不会等待命令完成,Run会阻塞等待命令完成。
下面看一下两个命令的区别:
func main() {
log.Println("start")
cmd := exec.Command("sleep", "10")
// 执行到此处时会阻塞等待10秒
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
log.Println("end")
}func main() {
log.Println("start")
cmd := exec.Command("sleep", "10")
// 如果用start则直接向后运行
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
log.Println("end")
// 执行Start会在此处等待10秒
err = cmd.Wait()
if err != nil {
log.Fatal(err)
}
log.Println("wait")
}// StderrPipe返回一个pipe,这个管道连接到command的标准错误,当command命令退出时,wait将关闭这些pipe
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
// StdinPipe返回一个连接到command标准输入的管道pipe
func (c *Cmd) StdinPipe() (io.WriteCloser, error)
// StdoutPipe返回一个连接到command标准输出的管道pipe
func (c *Cmd) StdoutPipe() (io.ReadCloser, error)package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
fmt.Println(err)
}
_, err = stdin.Write([]byte("tmp.txt"))
if err != nil {
fmt.Println(err)
}
stdin.Close()
// 终端标准输出tmp.txt
cmd.Stdout = os.Stdout
}package main
import (
"io/ioutil"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("ls", "-l", "/opt/software/")
// 获取输出对象,可以从该对象中读取输出结果
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
// 保证关闭输出流
defer stdout.Close()
// 运行命令
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
// 读取输出结果
if opBytes, err := ioutil.ReadAll(stdout); err != nil {
log.Fatal(err)
} else {
log.Println(string(opBytes))
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
}package main
import (
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("ls", "-l", "/opt/software/")
stdout, err := os.OpenFile("stdout.log", os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
log.Fatalln(err)
}
defer stdout.Close()
// 重定向标准输出到文件
cmd.Stdout = stdout
// 执行命令
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
}func main() {
log.Println("start")
CanKillRun()
log.Println("end")
}
// 执行完发挥的数据结构
type result struct {
err error
output []byte
}
// 能够杀死的进程
func CanKillRun() {
var (
cmd *exec.Cmd
ctx context.Context
cancelFunc context.CancelFunc
resultChan chan *result
res *result
)
// 创建一个通道用户协程交换数据
resultChan = make(chan *result, 1000)
// 拿到这个上下文的取消方法
ctx, cancelFunc = context.WithCancel(context.TODO())
// 起一个goroutine可以理解是子进程去处理
go func() {
var (
output []byte
err error
)
cmd = exec.CommandContext(ctx, "bash", "-c", "sleep 3;echo hello;")
// 执行任务,捕捉输出
output, err = cmd.CombinedOutput()
// 把任务执行结果输出给main协程
resultChan <- &result{
err: err,
output: output,
}
}()
// 1s后我们就把他杀死
// 继续往下走
time.Sleep(1 * time.Second)
// 取消上下文
cancelFunc()
// 读取通道里面的数据
res = <-resultChan
// 打印结果
fmt.Println("err: ", res.err, " out: ", string(res.output))
}/*
test.sh脚本内容
#!/bin/bash
for k in $( seq 1 10 )
do
echo "Hello World $k"
sleep 1
done
*/
var contentArray = make([]string, 0, 5)
func main() {
command := "/bin/bash"
params := []string{"-c", "sh test.sh"}
execCommand(command, params)
}
func execCommand(commandName string, params []string) bool {
contentArray = contentArray[0:0]
cmd := exec.Command(commandName, params...)
// 显示运行的命令
fmt.Printf("执行命令: %s\n", strings.Join(cmd.Args, " "))
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Fprintln(os.Stderr, "error=>", err.Error())
return false
}
// Start开始执行包含的命令,但并不会等待该命令完成即返回
// wait方法会返回命令的返回状态码并在命令返回后释放相关的资源
cmd.Start()
reader := bufio.NewReader(stdout)
var index int
// 实时循环读取输出流中的一行内容
for {
line, err2 := reader.ReadString('\n')
if err2 != nil || io.EOF == err2 {
break
}
fmt.Println(line)
index++
contentArray = append(contentArray, line)
}
cmd.Wait()
return true
}原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。