golang标准库os/exec使用方法示例


文章目录

  • 前言
  • 一、os/exec使用步骤
  • 二、常用方法
    • 1.创建实例常用方法
    • 2.调用实例常用方法
    • [3. 示例一、只执行命令,不获取结果](#3. 示例一、只执行命令,不获取结果)
    • [4. 示例二、执行命令,并获取结果](#4. 示例二、执行命令,并获取结果)
    • [5. 获取进程退出状态码ExitCode](#5. 获取进程退出状态码ExitCode)
    • [6. 查找二进制文件](#6. 查找二进制文件)
    • [7. 执行命令,并区分stdout 和 stderr](#7. 执行命令,并区分stdout 和 stderr)
    • [8. 使用管道实现多条命令组合](#8. 使用管道实现多条命令组合)
    • [9. 将命令的输出结果重定向到文件中](#9. 将命令的输出结果重定向到文件中)
    • [10. 终止进程](#10. 终止进程)
  • 总结

前言

在自定义运维工具开发过程中,有时候为了降低代码复杂度,能用shell脚本或linux命令实现的,我们尽量选择此方式然后通过代码去调用执行,那么 os/exec 这个系统库刚好提供了相应的功能。它提供了一组函数和结构,用于调用外部程序,这些外部程序可以是系统自带的,也可以是用户自定义的。并且包中提供了一组函数,用于执行系统命令,我们可以使用它来执行系统的cmd命令行。

exec包执行外部命令,它将 os.StartProcess 进行包装使得它更容易映射到 stdin 和 stdout,并且利用 pipe 连接i/o。
参考文档: https://pkg.go.dev/os/exec


一、os/exec使用步骤

yaml 复制代码
使用os/exec包执行命令的基本步骤如下:
	1、导入os/exec包

	2、创建一个exec.Cmd实例,指定要执行的命令和参数

	3、调用Cmd实例的Run、Start或Output、CombinedOutput等方法来执行命令
yaml 复制代码
os/exec包提供了多种方法来捕获命令的输出:
	1、CombinedOutput: 
		执行命令并返回标准输出和错误输出合并的切片。

	2、Output:
		 执行命令并返回标准输出的切片。

	3、StdoutPipe和StderrPipe:
		 返回与命令标准输出和错误输出关联的管道。

二、常用方法

1.创建实例常用方法

代码如下(示例):

go 复制代码
func Command(name string, arg ...string) *Cmd {}
使用 exec.Command 函数来创建一个 Cmd 结构体,该函数接受两个参数,第一个参数是要执行的命令,第二个
参数是命令行参数,比如 df -Th那么第一个参数就是 df,第二个参数就是 -Th

2.调用实例常用方法

代码如下(示例):

go 复制代码
//运行命令不返回标注输出1--阻塞
func (c *Cmd) Run() error {}
使用 Run 函数来执行这个命令,Run 函数会根据我们传入的参数来执行命令,并返回一个 error 类型的结果
注意:
	1、实际Run函数底层调用的是Start()和Wait()方法,因此它是阻塞的,只有等待命令执行完成才会返回结果
	2、一个 command 只能使用 Start() 或者 Run() 中的一个启动命令,不能两个同时使用
go 复制代码
//运行命令不返回标注输出2--非阻塞
func (c *Cmd) Start() error
使某个命令开始执行,但是并不等到他执行结束,这点和Run命令有区别
注意:
	1、Start方法 要和 Wait方法 一起使用
	2、Start 执行不会等待命令完成,是非阻塞的,Run会阻塞等待命令完成
	3、wait方法会返回命令的返回状态码并在命令返回后释放相关的资源
go 复制代码
//运行命令并返回其标准输出--阻塞
func (c *Cmd) Output() ([]byte, error)
go 复制代码
// 运行命令,并返回标准输出和标准错误--阻塞
func (c *Cmd) CombinedOutput() ([]byte, error)
注意: 
	Output() 和 CombinedOutput() 不能够同时使用,因为 command 的标准输出只能有一个,同时使用的话,便会报错。
go 复制代码
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipe返回一个pipe,这个管道连接到command的标准错误,当command命令退出时,wait将关闭这些pipe

func (c *Cmd) StdinPipe() (io.WriteCloser, error)
StdinPipe返回一个连接到command标准输入的管道pipe

func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
StdoutPipe返回一个连接到command标准输出的管道pipe

3. 示例一、只执行命令,不获取结果

代码如下(示例):

go 复制代码
package main

import (
	"fmt"
	"os/exec"
)

func main() {
    //创建实例
	cmd := exec.Command("df", "-Th")
    //调用Run()方法,执行到此处时会阻塞等待结果
	err := cmd.Run()
	if err != nil {
		fmt.Println("Error executing command:", err.Error())
	}
}

4. 示例二、执行命令,并获取结果

代码如下(示例):

go 复制代码
package main

import (
	"fmt"
	"os/exec"
)

func main() {
	cmd := exec.Command("ls", "-l", "/tmp/")
    //返回一个[]byte类型和错误
	out, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Printf("combined out:\n%s\n", string(out))
	}
	fmt.Printf("combined out:\n%s\n", string(out))
}

5. 获取进程退出状态码ExitCode

代码如下(示例):

go 复制代码
package main

import (
	"fmt"
	"log"
	"os/exec"
)

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)
}

6. 查找二进制文件

代码如下 (示例):

go 复制代码
package main

import (
	"fmt"
	"os/exec"
)

func main() {
	f, err := exec.LookPath("ls")
	if err != nil {
		fmt.Println(err)
	}
	// /usr/bin/ls
	fmt.Println(f)
}

7. 执行命令,并区分stdout 和 stderr

代码如下 (示例):

go 复制代码
package main

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
)

func main() {
    // 因为 exec.Command 不会自动展开通配符*,它会将通配符*作为一个普通的字符串传递给外部命令
	// sh -c 会启动一个新的shell并执行命令字符串,使用 shell 来执行命令,这样可以正确处理通配符
	cmd := exec.Command("sh", "-c", "ls -l /var/log/*.log")

	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout  // 标准输出
	cmd.Stderr = &stderr  // 标准错误

	err := cmd.Run()
	outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
	fmt.Printf("out:\n%s\nerr:\n%s\n", outStr, errStr)

	if err != nil {
		log.Fatalf("cmd.Run() failed with %s\n", err)
	}
}

8. 使用管道实现多条命令组合

代码如下 (示例):

go 复制代码
在shell中我们可以执行grep "INFO" /tmp/test.log |wc -l命令,
但是在代码中如果使用 exec.Command("grep", "INFO", "/tmp/test.log","|wc -l")这个来定义,执行会报错的,因此得使用到管道

package main
import (
    "os"
    "os/exec"
)
func main() {
    c1 := exec.Command("grep", "INFO", "/tmp/test.log")
    c2 := exec.Command("wc", "-l")
    c2.Stdin, _ = c1.StdoutPipe()
    c2.Stdout = os.Stdout
    _ = c2.Start()
    _ = c1.Run()
    _ = c2.Wait()
}

9. 将命令的输出结果重定向到文件中

代码如下 (示例):

go 复制代码
package main
import (
    "log"
    "os"
    "os/exec"
)
func main() {
    cmd := exec.Command("ls", "-l", "/tmp/")
    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)
    }
}

10. 终止进程

代码如下 (示例):

go 复制代码
在shell中通过kill -9 pid来实现

package main

import (
	"fmt"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	// 创建并启动新进程
	cmd := exec.Command("sleep", "5")
	err := cmd.Start()
	if err != nil {
		fmt.Println("Failed to start process:", err)
		return
	}

	fmt.Println("New process started. PID:", cmd.Process.Pid)

	// 等待进程完成
	err = cmd.Wait()
	if err != nil {
		fmt.Println("Process finished with error:", err)
		return
	}

	fmt.Println("Process finished successfully.")

	// 获取上述进程pid,然后终止进程
	err = terminateProcess(cmd.Process.Pid)
	if err != nil {
		fmt.Println("Failed to terminate process:", err)
		return
	}

	fmt.Println("Process terminated.")
}

// 终止进程
func terminateProcess(pid int) error {
	process, err := os.FindProcess(pid)
	if err != nil {
		return err
	}

	// 使用系统调用发送终止信号
	err = process.Signal(syscall.SIGTERM)
	if err != nil {
		return err
	}
	return nil
}

总结

以上就是随手整理的os/exec标准库部分方法的使用示例,有空继续补充

相关推荐
技术小齐18 分钟前
网络运维学习笔记 016网工初级(HCIA-Datacom与CCNA-EI)PPP点对点协议和PPPoE以太网上的点对点协议(此处只讲华为)
运维·网络·学习
ITPUB-微风25 分钟前
Service Mesh在爱奇艺的落地实践:架构、运维与扩展
运维·架构·service_mesh
落幕38 分钟前
C语言-进程
linux·运维·服务器
chenbin5201 小时前
Jenkins 自动构建Job
运维·jenkins
java 凯1 小时前
Jenkins插件管理切换国内源地址
运维·jenkins
AI服务老曹1 小时前
运用先进的智能算法和优化模型,进行科学合理调度的智慧园区开源了
运维·人工智能·安全·开源·音视频
sszdzq3 小时前
Docker
运维·docker·容器
book01213 小时前
MySql数据库运维学习笔记
运维·数据库·mysql
bugtraq20214 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
xmweisi4 小时前
【华为】报文统计的技术NetStream
运维·服务器·网络·华为认证