背景

  • golang可以获取命令执行的输出结果,但要执行完才能够获取。
  • 如果执行的命令是ssh,我们要实时获取,并执行相应的操作呢?

示例

func main() {
	user := "root"
	host := "172.16.116.133"
	//获取执行命令
	cmd := exec.Command("ssh", fmt.Sprintf("%s@%s", user, host))
	cmd.Stdin = os.Stdin
	var wg sync.WaitGroup
	wg.Add(2)
	//捕获标准输出
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("ERROR:", err)
		os.Exit(1)
	}
	readout := bufio.NewReader(stdout)
	go func() {
		defer wg.Done()
		GetOutput(readout)
	}()
	//捕获标准错误
	stderr, err := cmd.StderrPipe()
	if err != nil {
		fmt.Println("ERROR:", err)
		os.Exit(1)
	}
	readerr := bufio.NewReader(stderr)
	go func() {
		defer wg.Done()
		GetOutput(readerr)
	}()
	//执行命令
	cmd.Run()
	wg.Wait()
	return
}
func GetOutput(reader *bufio.Reader) {
	var sumOutput string				//统计屏幕的全部输出内容
	outputBytes := make([]byte, 200)
	for {
		n, err := reader.Read(outputBytes)		//获取屏幕的实时输出(并不是按照回车分割,所以要结合sumOutput)
		if err != nil {
			if err == io.EOF {
				break
			}
			fmt.Println(err)
			sumOutput += err.Error()
		}
		output := string(outputBytes[:n])
		fmt.Print(output) //输出屏幕内容
		sumOutput += output
	}
	return
}

应用场景

ssh是交互式命令,本示例实现了实时获取输出结果,并判断输出结果中有没有报错,报错则重试(再次登陆)。
场景:本Demo只是把"错误"二字视为异常,然后重试,实际上比这复杂的多,比如ssh连接超时重试等,这个逻辑请自行补充。

package main
import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"
)
func main(){
	retryTimes := 3
	var retryInterval time.Duration = 3
	user := "root"
	host := "172.16.116.133"
	//部分场景下重试登录
	shouldRetry := true
	for i:=1;i<=retryTimes && shouldRetry;i++{
		//执行命令
		shouldRetry = RunSSHCommand(user,host)
		if !shouldRetry{
			return
		}
		time.Sleep(retryInterval * time.Second)
	}
	if shouldRetry{
		fmt.Println("\n失败,请重试或检查")
	}
}
func shouldRetryByOutput(output string)bool{
	if strings.Contains(output,"错误"){		//匹配到"错误"就重试.这里只是Demo,请根据实际情况设置。
		return true
	}
	return false
}
func GetAndFilterOutput(reader *bufio.Reader)(shouldRetry bool){
	var sumOutput string
	outputBytes:= make([]byte,200)
	for {
		n,err := reader.Read(outputBytes)
		if err!=nil{
			if err == io.EOF{
				break
			}
			fmt.Println(err)
			sumOutput += err.Error()
		}
		output := string(outputBytes[:n])
		fmt.Print(output)		//输出屏幕内容
		sumOutput += output
		if shouldRetryByOutput(output){
			shouldRetry = true
		}
	}
	if shouldRetryByOutput(sumOutput){
		shouldRetry = true
	}
	return
}
func RunSSHCommand(user,host string)(shouldRetry bool){
	//获取执行命令
	cmd := exec.Command("ssh",fmt.Sprintf("%s@%s",user,host))
	cmd.Stdin = os.Stdin
	var wg sync.WaitGroup
	wg.Add(2)
	//捕获标准输出
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("ERROR:",err)
		os.Exit(1)
	}
	readout := bufio.NewReader(stdout)
	go func() {
		defer wg.Done()
		shouldRetryTemp := GetAndFilterOutput(readout)
		if shouldRetryTemp{
			shouldRetry = true
		}
	}()
	//捕获标准错误
	stderr, err := cmd.StderrPipe()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	readerr := bufio.NewReader(stderr)
	go func() {
		defer wg.Done()
		shouldRetryTemp := GetAndFilterOutput(readerr)
		if shouldRetryTemp{
			shouldRetry = true
		}
	}()
	//执行命令
	cmd.Run()
	wg.Wait()
	return
}

那年,郭少在京城。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。