关于如何在Windows下通过Golang调用cmd指令
起因#
目前我的项目中,当并发量提高的时候会出现UDP的buffer queue full的情况,我怀疑是因为UDP端口释放太慢导致堆积。
于是就打算用golang写一个命令行程序,每个0.5s执行以下cmd语句:
netstat -an | grep "UDP"
过程#
我记得golang是有一个exec库的,专门用来跑cmd,但是忘记在哪里看到了,就去网上搜。
首先是网上的教程大多是linux环境下的,所以他们的博客对与windows下的我并不通用。
这一点我试了很久...
One:#
使用网上类似这样的代码:
func main() {
c1 := exec.Command("netstat", "-an")
c2 := exec.Command("grep", "UDP")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
err := c1.Run()
if err != nil {
log.Panicln(err.Error())
}
_ = c2.Wait()
}
你就会收获如下错误
就很懵。后来想想是因为grep
是linux下的,之后就找到了windows下的代替品find
Two:#
之后代码就变成下面这样:
func main() {
c1 := exec.Command("find", "UDP")
output, err := c1.CombinedOutput()
if err != nil {
fmt.Println(fmt.Sprint(err) + ": " + string(output))
return
}
fmt.Println(string(output))
}
然而你运行之后会得到以下错误:
???这是个什么错误,你倒是告诉我错误是啥啊
后来去stack overflow上找到了方法
简而言之就是,这个err会返回在cmd.Stderr
上面,所以需要打印cmd.Stderr
。或者调用CombinedOutput()
。
Three:#
但是你调用之后,会得到以下结果:
???为什么会有乱码
查资料发现是因为cmd的输入输出都是遵循GBK
编码的,但是中文是UTF-8
编码的,所以中文就会显示乱码。
艹cmd是什么落后的东西啊。
之后去网上嫖了一个GBK转UTF-8的:
func ConvertByte2String(byte []byte, charset string) string {
var str string
switch charset {
case "GB18030":
var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
str = string(decodeBytes)
case "UTF8":
fallthrough
default:
str = string(byte)
}
return str
}
最后总算能显示中文了,但是显示的结果如下:
Four:#
我看cmd都是这么用的怎么到这就拉垮了?
之后通过打印语句,看了一下以上的命令到底是什么:
猜测可能是因为没有引号导致的,于是就加上引号,代码现在长这样:
func main() {
c1 := exec.Command("netstat", "-an")
c2 := exec.Command("find", "\"UDP\"", "/c")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
err := c1.Run()
if err != nil {
log.Panicln(err.Error())
}
_ = c2.Wait()
}
发现貌似卡住了,根本没输出。
Five:#
debug模式进入之后发现是卡在了c2.Wait()这里,这个我也不知道为什么。于是就去网上换了一种方式,来写。
突然看到一篇博客说可以直接通过调用cmd.exe之后设置参数就可以直接一句话完成我最开始想要的cmd语句。
于是现在长这样:
func main() {
c1 := exec.Command("cmd.exe", "netstat -an | find /c \"UDP\"")
output, err := c1.CombinedOutput()
fmt.Println(c1.String())
if err != nil {
fmt.Println(fmt.Sprint(err) + ": " + ConvertByte2String(output, "GB18030"))
return
}
fmt.Println(string(output))
}
结果报了这样的错误:
?我寻思这不就是字符串参数。这个奇奇怪怪的是什么??
之后又尝试了这种写法:
c1 := exec.Command("cmd.exe", "netstat -an | find /c UDP")
结果还是会报错...
有可能是因为cmd的转义字符跟golang的有所不同导致的,于是我采用了raw string的形式
c1 := exec.Command("cmd.exe", `netstat -an | find /c "UDP"`)
c1 := exec.Command("cmd.exe", `netstat -an | find /c UDP`)
Six:#
就在我感到绝望的时候,我看到网上说powershell完美兼容cmd同时还会有更智能地识别,于是我就看怎么用golang调用powershell,发现只需要把之前的cmd改成powershell就可以:
c1 := exec.Command("powershell.exe", `netstat -an | find /c UDP`)
结果还是不对,之后我去网上搜:搜到了结果
https://blog.csdn.net/beebeeyoung/article/details/116677249
c1 := exec.Command("powershell.exe", "netstat -an | find /c `\"UDP`\"")
终于!!!!总算成功!!!!
完整代码如下:
package main
import (
"golang.org/x/text/encoding/simplifiedchinese"
"os/exec"
)
func main() {
c1 := exec.Command("powershell.exe", "netstat -an | find /c `\"UDP`\"")
output, _ := c1.CombinedOutput()
println(ConvertByte2String(output, "GB18030"))
}
func ConvertByte2String(byte []byte, charset string) string {
var str string
switch charset {
case "GB18030":
var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
str = string(decodeBytes)
case "UTF8":
fallthrough
default:
str = string(byte)
}
return str
}
完结撒花!!!
尾声#
最后发现Prometheus地node-exporter里面自带监听端口的metric,所以只需要在grafana里面填上PromQL就可以(
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
2021-11-08 Atcoder Beginner Contest 226
2019-11-08 计蒜客练习题:n个最小和(思想+优先队列)
2019-11-08 自定义类型的优先队列