在Golang中执行Shell命令的教程详解
作者:宇宙之一粟 发布时间:2024-04-25 15:10:51
Exec 包
我们可以使用官方的 os/exec 包来运行外部命令。
当我们执行 shell 命令时,我们是在 Go 应用程序之外运行代码。为此,我们需要在子进程中运行这些命令。
每个命令都作为正在运行的 Go 应用程序中的子进程运行,并公开我们可以用来从进程读取和写入数据的 Stdin 和 Stdout 属性。
运行基本的 Shell 命令
要运行一个简单的命令并读取其输出,我们可以创建一个新的 *exec.Cmd 实例并运行它。在此示例中,让我们使用 ls 列出当前目录中的文件,并打印代码的输出:
// 创建了一个新的 *Cmd 实例
// 使用 "ls" 命令和 "./" 参数作为参数
cmd := exec.Command("ls", "./")
// 使用 `Output` 方法执行该命令并收集其输出
out, err := cmd.Output()
if err != nil {
// 如果执行命令时出现错误,则输出错误信息
fmt.Println("could not run command: ", err)
}
// 否则,输出运行该命令的输出结果
fmt.Println("Output: ", string(out))
由于我在示例仓库中运行此代码,因此它会打印项目根目录中的文件:
> go run shellcommands/main.go
Output: LICENSE
README.md
go.mod
shellcommands
请注意,当我们运行 exec
时,我们的应用程序不会生成 shell,而是直接运行给定的命令。这意味着将不会执行任何基于 shell 的处理,例如 glob 模式或扩展。
执行持久运行的命令
前面的示例执行了 ls
命令,该命令立即返回了它的输出。那些输出是连续的,或者需要很长时间才能检索的命令呢?
例如,当我们运行 ping
命令时,我们会定期获得连续输出:
➜ ~ ping google.com
PING google.com (142.250.77.110): 56 data bytes
64 bytes from 142.250.77.110: icmp_seq=0 ttl=116 time=11.397 ms
64 bytes from 142.250.77.110: icmp_seq=1 ttl=116 time=17.646 ms ## this is received after 1 second
64 bytes from 142.250.77.110: icmp_seq=2 ttl=116 time=10.036 ms ## this is received after 2 seconds
64 bytes from 142.250.77.110: icmp_seq=3 ttl=116 time=9.656 ms ## and so on
# ...
如果我们尝试使用 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)
}
这段代码使用 Go 语言的 exec 包来执行 ping 命令并将输出重定向到标准输出流(os.Stdout)。具体来说,它创建了一个命令对象(cmd),该对象包含要执行的命令(“ping"和"google.com”)。然后将命令的标准输出流(cmd.Stdout)设置为应用程序的标准输出流(os.Stdout)。最后,使用 cmd.Run() 方法运行该命令,并等待其完成。如果运行命令时出现错误,将在控制台输出错误信息。
输出结果:
> go run shellcommands/main.go
PING google.com (142.250.195.142): 56 data bytes
64 bytes from 142.250.195.142: icmp_seq=0 ttl=114 time=9.397 ms
64 bytes from 142.250.195.142: icmp_seq=1 ttl=114 time=37.398 ms
64 bytes from 142.250.195.142: icmp_seq=2 ttl=114 time=34.050 ms
64 bytes from 142.250.195.142: icmp_seq=3 ttl=114 time=33.272 ms
# ...
# and so on
通过直接分配 Stdout
属性,我们可以捕获整个命令生命周期的输出,并在收到后立即处理。
自定义输出写入程序
与使用 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
}
现在我们可以指定一个新的 customWriter
实例作为输出写入器:
cmd.Stdout = customOutput{}
如果我们现在运行应用程序,我们将得到以下输出:
received output: PING google.com (142.250.195.142): 56 data bytes
64 bytes from 142.250.195.142: icmp_seq=0 ttl=114 time=187.825 ms
received output: 64 bytes from 142.250.195.142: icmp_seq=1 ttl=114 time=19.489 ms
received output: 64 bytes from 142.250.195.142: icmp_seq=2 ttl=114 time=117.676 ms
received output: 64 bytes from 142.250.195.142: icmp_seq=3 ttl=114 time=57.780 ms
使用 STDIN 将输入传递给命令
在前面的示例中,我们在不提供任何输入(或提供有限的输入作为参数)的情况下执行命令。在大多数情况下,输入是通过 STDIN
流给出的。
译注:就是外部给命令,然后去执行
一个著名的例子是 grep
命令,我们可以通过管道从另一个命令输入:
➜ ~ echo "1. pear\n2. grapes\n3. apple\n4. banana\n" | grep apple
3. apple
在这里,输入通过 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
Kill 一个子进程
有几个命令会无限期地运行,或者需要明确的信号才能停止。
例如,如果我们使用 python3 -m http.server 启动 Web 服务器或执行 sleep 10000,则生成的子进程将运行很长时间(或无限运行)。
要停止这些进程,我们需要从应用程序发送终止信号。我们可以通过向命令添加一个上下文实例来做到这一点。
如果上下文被取消,命令也会终止。
ctx := context.Background()
// The context now times out after 1 second
// alternately, we can call `cancel()` to terminate immediately
ctx, cancel = context.WithTimeout(ctx, 1*time.Second)
cmd := exec.CommandContext(ctx, "sleep", "100")
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:
当您想要限制运行命令所花费的时间或想要创建回退以防命令未按时返回结果时,终止子进程很有用。
来源:https://blog.csdn.net/yuzhou_1shu/article/details/130725713
猜你喜欢
- 目录前言连接管理额外连接管理端口总结前言下面这个报错,相信大多数童鞋都遇见过;那么碰到这个问题,我们应该怎么办呢?在MySQL 5.7及之前
- 1.原生js操作domconst dom = getElementById(‘box')2.vue官方方法:refvue中的ref是
- 前言Python环境的搭建这里就不赘述了,有需要的小伙伴可以在网上搜罗出很多教程,注意安装PyChom编辑工具。这次我们主要讲一下几点内容:
- Instr函数与InstrRev函数大家都应该很熟悉,但是如果你看过《ASP * 站开发实践教程》,你应该注意一下。该书中介绍它们时是很有迷
- 0. 学习目标单链表只有一个指向直接后继的指针来表示结点间的逻辑关系,因此可以方便的从任一结点开始查找其后继结点,但要找前驱结点则比较困难,
- torch.autograd.backward(variables, grad_variables=None, retain_graph=N
- 疫情还没结束,小编只能宅在家里,哪哪也去不了,今天突发奇想给大家分享一篇教程关于Python paramiko 模块浅谈与SSH主要功能模拟
- centos6自带python2.6版本,根据需要,安装python2.7、easy_install-2.7、pip2.7依赖yum gro
- MySQL是一个小巧玲珑但功能强大的数据库,目前十分流行。但是官网给出的安装包有两种格式,一个是msi格式,一个是zip格式的。很多人下了z
- show parameter processes; 然后 更改系统连接数 alter system set processes=1000 s
- 前言Go语言做网络开发是非常容易的一件事,它已经为我们封装好了Http包,开箱即用。除此之外,我们也可以用Gin框架或者使用fasthttp
- python可以在处理各种数据时,如果可以将这些数据,利用图表将其可视化,这样在分析处理起来,将更加直观、清晰,以下是 利用 PyEchar
- WGAN与GAN的不同去除sigmoid使用具有动量的优化方法,比如使用RMSProp要对Discriminator的权重做修整限制以确保l
- 现象:已知,连接的WIFI网络需要通过代理服务器才能连接外网,按照正常的程序无法发送邮件,而直连一个没有代理的网络【如自己的wifi热点】,
- 客户端用一个html页面调用一个ashx文件(一般http处理程序),返回 json格式的自定义对象: html: <!DOCTYPE
- 例子:以百度文库中选择文档的类型为例问题一:遍历点击所有文档类型的单选框# coding=utf-8from selenium import
- 一、问题背景无人机在拍摄视频时,由于风向等影响因素,不可避免会出现位移和旋转,导致拍摄出的画面存在平移和旋转的帧间变换, 即&ldq
- 1. 引言元组是Python中一种重要的内置数据类型。与列表一样,我们经常使用元组将多个对象保存为相应的数据容器。然而,与列表不同的是元组的
- 前言人脸识别在LWF(Labeled Faces in the Wild)数据集上人脸识别率现在已经99.7%以上,这个识别率确实非常高了,
- 正文之前上午给爸爸打了个电话庆祝他50岁生日,在此之前搞了个大扫除,看了会知乎,到实验室已经十一点多了。约喜欢的妹子吃饭失败,以至于工作积极