Golang应用执行Shell命令实战教程

news2025/1/13 17:02:17

本文学习如何在Golang程序中执行Shell命令(如,ls,mkdir或grep),如何通过stdin和stdout传入I/O给正在运行的命令,同时管理长时间运行的命令。为了更好的理解,针对不同场景由浅入深提供几个示例进行说明,希望你能轻松理解。

exec包

使用官方os/exec包可以执行外部命令,当你执行shell命令,是需要在Go应用的外部运行代码,因此需要这些命令在子进程中运行。如下图所示:
在这里插入图片描述

每个命令在Go应用中作为子进程运行,并暴露stdin和stdout属性,我们可以使用它们读写进程数据。

运行基本Shell命令

运行简单命令并从它的输出中读取数据,通过创建*exec.Cmd实例实现。在下面示例中,使用ls列出当前目录下的文件,并从代码中打印其输出:

// create a new *Cmd instance
// here we pass the command as the first argument and the arguments to pass to the command as the
// remaining arguments in the function
cmd := exec.Command("ls", "./")

// The `Output` method executes the command and
// collects the output, returning its value
out, err := cmd.Output()
if err != nil {
  // if there was any error, print it here
  fmt.Println("could not run command: ", err)
}
// otherwise, print the output from running the command
fmt.Println("Output: ", string(out))

因为在当前目录下运行程序,因此输出项目根目录下文件:

> go run shellcommands/main.go

Output:  LICENSE
README.md
command.go

在这里插入图片描述
当运行exec,程序没有产生shell,而是直接运行给定命令,这意味着不会进行任何基于shell的处理,比如glob模式或扩展。举例,当运行ls ./*.md命令,并不会如我们在那个shell中运行命令一样输出readme.md

执行长时间运行命令

前面示例执行ls命令立刻返回结果,但当命令输出是连续的、或需要很长时间执行时会怎样呢?举例,运行ping命令,会周期性获得连续结果:

ping www.baidu.com 
PING www.a.shifen.com (36.152.44.95) 56(84) bytes of data.
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=1 ttl=128 time=11.1 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=2 ttl=128 time=58.8 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=3 ttl=128 time=28.2 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=4 ttl=128 time=11.1 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=5 ttl=128 time=11.5 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=6 ttl=128 time=53.6 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=7 ttl=128 time=10.2 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=8 ttl=128 time=10.4 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=9 ttl=128 time=15.8 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=10 ttl=128 time=16.5 ms
64 bytes from 36.152.44.95 (36.152.44.95): icmp_seq=11 ttl=128 time=10.9 ms
^C64 bytes from 36.152.44.95: icmp_seq=12 ttl=128 time=9.92 ms

如果尝试使用cmd.Output执行这类命令,则不会获得任何结果,因为Output方法等待命令执行结束,而ping无限期执行。因此需要自定义Stdout属性去读取连续输出:

cmd := exec.Command("ping", "google.com")

// pipe the commands output to the applications
// standard output
cmd.Stdout = os.Stdout

// Run still runs the command and waits for completion
// but the output is instantly piped to Stdout
if err := cmd.Run(); err != nil {
  fmt.Println("could not run command: ", err)
}

再次运行程序,输出结果于Shell中执行类似。

通过直接分配Stdout属性,我们可以在整个命令生命周期中捕获输出,并在接收到输出后立即对其进行处理。进程间io交互如下图所示:

在这里插入图片描述

自定义写输出

代替使用os.Stdout,还能通过实现io.Writer接口创建自定义写输出。

下面自定义代码在每个输出块前增加"received output: "前缀:

type customOutput struct{}

func (c customOutput) Write(p []byte) (int, error) {
	fmt.Println("received output: ", string(p))
	return len(p), nil
}

现在给命令输出赋值自定义写输出实例:

cmd.Stdout = customOutput{}

再次运行程序,会获得下面的输出。

使用Stdin给命令传递输入

前面示例没有给命令任何输入(或提供有限输入作为参数),大多数场景中通过Stdin流传递输入信息。典型的示例为grep命令,可以通过管道从一个命令串给另一个命令:

➜  ~ echo "1. pear\n2. grapes\n3. apple\n4. banana\n" | grep apple
3. apple

这里echo的输出作为stdin传给grep,输入一组水果,通过grep过滤仅输出apple.

*Cmd实例提供了输入流用于写入,下面实例使用它传递输入给grep子进程:

cmd := exec.Command("grep", "apple")

// Create a new pipe, which gives us a reader/writer pair
reader, writer := io.Pipe()
// assign the reader to Stdin for the command
cmd.Stdin = reader
// the output is printed to the console
cmd.Stdout = os.Stdout

go func() {
  defer writer.Close()
  // the writer is connected to the reader via the pipe
  // so all data written here is passed on to the commands
  // standard input
  writer.Write([]byte("1. pear\n"))
  writer.Write([]byte("2. grapes\n"))
  writer.Write([]byte("3. apple\n"))
  writer.Write([]byte("4. banana\n"))
}()

if err := cmd.Run(); err != nil {
  fmt.Println("could not run command: ", err)
}

输出结果:

3. apple

在这里插入图片描述

结束子进程

有一些命令无限期运行,需要能够显示信号去结束。举例,如果使用python3 -m http.server运行web服务或sleep 10000,则子进程会运行很长时间或无限期运行。

要停止进程,需要从应用中发送kill信号,可以通过给命令增加上下文实例实现。如果上下文取消,则命令也会终止执行:

ctx := context.Background()
// The context now times out after 1 second
// alternately, we can call `cancel()` to terminate immediately
ctx, _ = context.WithTimeout(ctx, 1*time.Second)

// sleep 10 second 
cmd := exec.CommandContext(ctx, "sleep", "10")

out, err := cmd.Output()
if err != nil {
  fmt.Println("could not run command: ", err)
}
fmt.Println("Output: ", string(out))

运行程序,1秒后输出结果:

could not run command:  signal: killed
Output:  

当需要在有限时间内运行命令或在一定时间内命令没有返回结果则执行备用逻辑。

总结

到目前为止,我们已经学习了多种执行unix shell命令和与之交互的方法。下面是使用os/exec包时需要注意的一些事情:

  • 当您希望执行通常不提供太多输出的简单命令时使用cmd.Output
  • 对于具有连续或长时间输出的函数应使用cmd.Run,并通过cmd.Stdout和cmd.Stdin与之交互
  • 在生产场景中,如果进程在给定时间内没有响应,须有超时并结束功能,可以使用取消上下文发送终止命令

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/178861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

77、TensoRF: Tensorial Radiance Fields

简介 主页:https://apchenstu.github.io/TensoRF/ 总体而言,该文章主要内容于DVGO类似 将场景的亮度场建模为4D张量,它表示一个具有每体素多通道特征的3D体素网格,中心思想是将4D场景张量分解为多个紧凑低秩张量分量&#xff0c…

06 | 要找工作了,应该如何准备?

前言 前言:找工作更像相亲,总有一款适合自己。简历就像一份广告,对方要什么你写什么,而不是你有什么。 文章目录前言一、找工作的流程二、做法1. 分析职位描述(JD)1)组成2)做法一、找…

【数据结构】7.2 线性表的查找

7.2.1 顺序查找(线性查找) 应用范围: 顺序表或线性链表表示的静态查找表。表内元素之间可以无序。 数据元素类型定义: 数据表可能有多个数据域的值,比如成绩表中有姓名、成绩、总分等。所以用结构类型来表示要存储…

背景颜色和背景图片

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <!--这行代码是告诉浏览器需要使用"utf-8"字符集打开 因为HBuilder工具是采用utf-8编码的 注意&#xff1a;并不是设置当前页面的字符集编码方式--> …

SpringMVC(十二):SpringMVC文件下载

文章目录 SpringMVC文件下载 一、下载之前的准备 二、下载的基本流程 三、下载的请求数据 1、下载的后台实现 2、下载的示例代码 SpringMVC文件下载 一、下载之前的准备 展示所有玩家信息&#xff0c;包括图片展示。 <% page contentType"text/html;charset…

Linux_常见命令

1.ls ls -l 列出隐藏文件&#xff0c;并显示10项权限,类似如同下图 在部分发行版本的linux下&#xff0c;ll等同于ls -l 首先&#xff0c;第一列为-则代表着这一列是文件&#xff0c; 第一列为d则代表这一列为目录 除了第一位&#xff0c;那么其他还有9位&#xff0c;分为3组…

学习shell与shell script 02_vim 的保存恢复与打开时的警告信息

[1] Stopped vim test.txt $kill -9 %1[1] Stopped vim test.txt $jobs [1] Killed vim test.txt编辑一个文件后&#xff0c;使用Ctrl z 挂起。然后被不正常中断&#xff0c;导致暂存盘无法通过正常的流程来结束。所以暂…

AHB协议(2/2)

Charpter 4 Bus Interconnection 4.1 互联结构 互联结构在系统中提供了M端和S端的连接。单一的M端只需要使用一个解码器和多路选择器。 一个多M端的系统中需要使用提供仲裁和将信号从不同的M端分发到对应的S端的互联结构。分发结构需要地址&#xff0c;控制信号和写数据。 4…

ARM uboot的常用命令

一、uboot 的常用命令1 1、类似 linux 终端的行缓冲命令行 (1) 行缓冲的意思就是&#xff1a;当我们向终端命令行输入命令的时候&#xff0c;这些命令没有立即被系统识别&#xff0c;而是被缓冲到一个缓存区&#xff08;也就是系统认为我们还没有输入完&#xff09;&#xff…

2.5 变量与常量

文章目录1.变量1.1 变量的声明1.2 变量的命名1.3 变量的初始化1.4 变量的使用2 常量1.变量 和其他的编程语言一样&#xff0c;JAVA也是用变量来存储值的&#xff0c;常量可以理解为一种特殊的变量&#xff0c;其特殊在它的值是不变的。 1.1 变量的声明 声明变量本质就是创造…

自动驾驶感知——环境感知的基本概念

文章目录1. 智能汽车概述1.1 汽车新四化1.2 智能网联汽车1.3 SAE J3016自动驾驶分级标准2. 环境感知定义2.1 智能网联汽车系统架构2.2 环境感知定义及对象3. 各类传感器的介绍4. 环境感知经典算法概述5. 自动驾驶仿真软件的功能5.1 背景介绍5.2 自动驾驶仿真软件平台背后的科学…

(二十六)Set系列集合

简介: Set集合&#xff0c;基础自Collection。特征是插入无序&#xff0c;不可指定位置访问。Set集合的实现类可说是基于Map集合去写的。通过内部封装Map集合来实现的比如HashSet内部封装了HashMap。Set集合的数据库不能重复&#xff08; 或 eqauls&#xff09;的元素Set集合的…

AxMath使用教程(持续更新中)

前言 这两天学了学Latex&#xff0c;主要是为了以后写毕业论文做铺垫&#xff0c;而且Latex在数学公式这一方面&#xff0c;要比Word方便许多&#xff0c;于是我就下载了一款国产的公式编辑器——AxMath。永久会员不贵&#xff0c;只要36元&#xff0c;而且软件很好用&#xf…

【自然语言处理】情感分析(一):基于 NLTK 的 Naive Bayes 实现

情感分析&#xff08;一&#xff09;&#xff1a;基于 NLTK 的 Naive Bayes 实现朴素贝叶斯&#xff08;Naive Bayes&#xff09;分类器可以用来确定输入文本属于某一组类别的概率。例如&#xff0c;预测评论是正面的还是负面的。 它是 “朴素的”&#xff0c;它假设文本中的单…

机器学习: Label vs. One Hot Encoder

如果您是机器学习的新手&#xff0c;您可能会对这两者感到困惑——Label 编码器和 One-Hot 编码器。这两个编码器是 Python 中 SciKit Learn 库的一部分&#xff0c;它们用于将分类数据或文本数据转换为数字&#xff0c;我们的预测模型可以更好地理解这些数字。今天&#xff0c…

图机器学习-节点嵌入(Node Embedding)

图机器学习-节点嵌入(Node Embedding) Node Embedding简单点来说就是将一个node表示为一个RdR^dRd的向量。 EncoderDecoder Framework 我们首先需要设计一个encoder对节点进行编码。既然要比较相似度那么我就需要定义节点的相似度。同时我们还需要定义一个decoder&#xff0…

Java线程的生命周期的五种状态

在java中&#xff0c;任何对象都要有生命周期&#xff0c;线程也不例外&#xff0c;它也有自己的生命周期。当Thread对象创建完成时&#xff0c;线程的生命周期便开始了&#xff0c;当run()方法中代码正常执行完毕或者线程抛出一个未捕获的异常(Exception)或者错误(Error)时&am…

通信原理简明教程 | 数字基带传输

文章目录1数字基带传输系统的基本组成2 数字基带信号及其频域特性2.1 基本码型2.2 常用码型2.3 数字基带信号的功率谱3 码间干扰3.1 码间干扰的概念&#xff08;ISI&#xff09;3.2 无码间干扰传输的条件3.3 无码间干扰的典型传输波形4 部分响应和均衡技术&#xff08;*&#x…

java+ssm网上书店图书销售评价系统

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 2 第3章 系统分析 4 3.1 需求分析 4 3.2 系统可行性分析 4 3.2.1技术可行性&#xff1a;技术背景 4 3.2.2经…

【蓝桥云课】位运算

一、原码、反码、补码 原码&#xff1a;符号位&#xff08;正数为0、负数为1&#xff09;二进制数 反码&#xff1a;正数的反码正数的原码&#xff1b;负数的反码负数的原码除符号位外按位求反 补码&#xff1a;正数的补码正数的反码&#xff1b;负数的补码负数的反码1 整数原…