Golang实现一个批量自动化执行树莓派指令的软件(2)指令

devtools/2024/11/13 9:43:03/

简介

基于上篇 Golang实现一个批量自动化执行树莓派指令的软件(1)文本加密&配置&命令行交互实现, 这篇实现的是指令, 即通过ssh执行linux指令的实现。

环境描述

运行环境: Windows, 基于Golang, 暂时没有使用什么不可跨平台接口, 理论上支持Linux/MacOS
目标终端树莓派DebianOS(主要做用它测试)

实现

接口定义

type ICommander interface {/*Command: 堵塞直到执行完毕*/Command(cmdStr string) (output string, err error)/*CommandWithCallback:background 为 true 时 异步函数, 执行完毕之后自动调用回调background 为 false时 堵塞直到执行完毕之后调用回调*/CommandWithCallback(cmdStr string, callback func(output string, err error), background bool) error/*Cancel : 任务取消, 执行到一半的任务断开场景*/Cancel() error/*Desroy : 无论如何直接关闭客户端*/Destroy() error
}

接口实现

package sshutilimport ("fmt""golang.org/x/crypto/ssh""io""time"
)type Commander struct {sshClient *ssh.Clientstarted  boolcanceled chan struct{}finished chan struct{}
}func NewCommander(cfg SSHConfig) (*Commander, error) {sshClient, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", cfg.IP, cfg.Port), cfg.sshClientConfig)if err != nil {return nil, err}return &Commander{sshClient: sshClient,canceled:  make(chan struct{}),finished:  make(chan struct{}),}, nil
}func (c *Commander) Command(cmd string) (output string, err error) {c.doCommand(cmd, func(recvStr string, e error) {output = recvStrerr = e})return
}func (c *Commander) CommandWithCallback(cmd string, callback func(output string, e error), background bool) (err error) {if background {go c.doCommand(cmd, callback)} else {c.doCommand(cmd, callback)}return
}func (c *Commander) doCommand(cmd string, callback func(output string, e error)) {var (session   *ssh.SessionrecvBytes []byteerr       errorcanceled  bool)if session, err = c.sshClient.NewSession(); err != nil {callback("", err)return}c.started = truego func(session *ssh.Session) {select {case <-c.finished:case <-c.canceled:canceled = trueif e := session.Signal(ssh.SIGINT); nil != err {fmt.Println("emit abort fail: ", e.Error())}}}(session)defer func() {c.started = falseif !canceled {c.finished <- struct{}{}}if e := session.Close(); nil != e && e != io.EOF {fmt.Println("session close fail: ", e.Error())}}()// err = session.Start(command) // ssh库的异步方式if recvBytes, err = session.CombinedOutput(cmd); err != nil {if canceled {err = fmt.Errorf("user canceled")}callback(string(recvBytes), err)return}callback(string(recvBytes), err)
}func (c *Commander) Cancel() error {if c.started {select {case c.canceled <- struct{}{}:case <-time.After(time.Second): // 取消时间过长,取消失败return fmt.Errorf("time out waiting for cancel")}}return nil
}func (c *Commander) Destroy() error {var err = c.Cancel()close(c.finished)close(c.canceled)err = c.sshClient.Close()return err
}

测试用例

package sshutilimport ("fmt""golang.org/x/crypto/ssh""sync""testing""time"
)func newCommander() (*Commander, error) {config := &ssh.ClientConfig{User: "pi",Auth: []ssh.AuthMethod{ssh.Password("a123456"),},HostKeyCallback: ssh.InsecureIgnoreHostKey(),}sshCfg := SSHConfig{IP:              "192.168.3.2",Port:            22,User:            "pi",Password:        "a123456",sshClientConfig: config,}commander, err := NewCommander(sshCfg)return commander, err
}func TestEasyCommandAndDestroy(t *testing.T) {commander, err := newCommander()if nil != err {fmt.Println("new command fail: ", err.Error())return}defer func() {e := commander.Destroy()if nil != err {fmt.Println("fail to destroy, ", e.Error())}}()var output stringoutput, err = commander.Command("ls /")fmt.Println("command result: ", output)
}func TestCommandWithCallbackWithoutAsync(t *testing.T) {commander, err := newCommander()if nil != err {fmt.Println("new command fail: ", err.Error())return}defer func() {e := commander.Destroy()if nil != err {fmt.Println("fail to destroy, ", e.Error())}}()err = commander.CommandWithCallback("sleep 4; ls /", func(output string, e error) {if nil != e {fmt.Println("fail, ", e.Error())} else {fmt.Println("result: ", output)}}, false)if nil != err {fmt.Println("do command fail: ", err.Error())return}
}func TestCommandWithCallbackAsync(t *testing.T) {var waiter sync.WaitGroupcommander, err := newCommander()if nil != err {fmt.Println("new command fail: ", err.Error())return}defer func() {e := commander.Destroy()if nil != err {fmt.Println("fail to destroy, ", e.Error())}}()waiter.Add(1)err = commander.CommandWithCallback("sleep 4; ls /", func(output string, e error) {if nil != e {fmt.Println("fail, ", e.Error())} else {fmt.Println("result: ", output)}waiter.Done()}, true)if nil != err {fmt.Println("do command fail: ", err.Error())return}fmt.Println("waiting finished<--------")waiter.Wait()fmt.Println("waiting finished------>")
}func TestCommandWithCallbackAndCancel(t *testing.T) {var waiter sync.WaitGroupcommander, err := newCommander()if nil != err {fmt.Println("new command fail: ", err.Error())return}defer func() {e := commander.Destroy()if nil != err {fmt.Println("fail to destroy, ", e.Error())}}()waiter.Add(1)err = commander.CommandWithCallback("sleep 10; ls /", func(output string, e error) {if nil != e {fmt.Println("fail, ", e.Error())} else {fmt.Println("result: ", output)}waiter.Done()}, true)if nil != err {fmt.Println("do command fail: ", err.Error())return}fmt.Println("waiting finished<--------")time.Sleep(time.Second * 2)fmt.Println("canceling...")fmt.Println("canceled, ", commander.Cancel())waiter.Wait()fmt.Println("waiting finished------>")
}

代码源

https://gitee.com/grayhsu/ssh_remote_access

其他

参考


http://www.ppmy.cn/devtools/17894.html

相关文章

Godot3D学习笔记1——界面布局简介

创建完成项目之后可以看到如下界面&#xff1a; Godot引擎也是场景式编程&#xff0c;这里的一个场景相当于一个关卡。 这里我们点击左侧“3D场景”按钮创建一个3D场景&#xff0c;现在在中间的画面中会出现一个球。在左侧节点视图中选中“Node3D”&#xff0c;右键创建子节点…

【UE5.1 C++】提升编译速度

步骤 1. 在“C:\Users\用户\AppData\Roaming\Unreal Engine\UnrealBuildTool”目录下找到“BuildConfiguration.xml”文件 打开“BuildConfiguration.xml”&#xff0c;添加如下部分内容 <?xml version"1.0" encoding"utf-8" ?> <Configuratio…

【AIGC调研系列】Bunny-Llama-3-8B-V与其他多模态大模型相比的优劣

Bunny-Llama-3-8B-V作为基于Llama-3的多模态大模型&#xff0c;其优势主要体现在以下几个方面&#xff1a; 性能超越其他模型&#xff1a;根据我搜索到的资料&#xff0c;Bunny-Llama-3-8B-V在多个主流Benchmark上表现良好&#xff0c;超越了LLaVA-7B、LLaVA-13B、Mini-Gemini…

PyCharm 中的特殊标记

在使用 PyCharm 开发 Python 项目的时候&#xff0c;经常会有一些特殊的标记&#xff0c;有些是 IDE 提示的代码规范&#xff0c;有些则为了方便查找而自定义的标记。 我在之前写过一些关于异常捕获的文章&#xff1a;Python3 PyCharm 捕获异常报 Too broad exception clause …

前端开发攻略---用原生JS在网页中也能实现语音识别

1、语音识别的过程 语音识别涉及三个过程&#xff1a;首先&#xff0c;需要设备的麦克风接收这段语音&#xff1b;其次&#xff0c;语音识别服务器会根据一系列语法 (基本上&#xff0c;语法是你希望在具体的应用中能够识别出来的词汇) 来检查这段语音&#xff1b;最后&#xf…

【汇编语言】直接定址表

【汇编语言】直接定址表 文章目录 【汇编语言】直接定址表前言一、移位指令移位指令过程逻辑移位指令shl 和 shr 二、操作显存数据显示的原理显示缓冲区的结构显示信息的一种“直接”方式 三、描述内存单元的标号关于标号去了冒号的数据标号数据标号同时描述内存地址和单元长度…

新兴游戏引擎Godot vs. 主流游戏引擎Unity和虚幻引擎,以及版本控制工具Perforce Helix Core如何与其高效集成

游戏行业出现一个新生事物——Godot&#xff0c;一个免费且开源的2D和3D游戏引擎。曾经由Unity和虚幻引擎&#xff08;Unreal Engine&#xff09;等巨头主导的领域如今迎来了竞争对手。随着最近“独特”定价模式的变化&#xff0c;越来越多的独立开发者和小型开发团队倾向于选择…

数据结构- 顺序表-单链表-双链表 --【求个关注!】

文章目录 一 顺序表代码&#xff1a; 二 链表单链表双向链表 一 顺序表 顺序表是线性表的一种 所谓线性表指一串数据的组织存储在逻辑上是线性的&#xff0c;而在物理上不一定是线性的 顺序表的底层实现是数组&#xff0c;其由一群数据类型相同的元素组成&#xff0c;其在逻辑…