Administrator
发布于 2023-04-17 / 353 阅读 / 0 评论 / 0 点赞

golang ssh 远程执行shell命令sftp下载文件

demo

package service

import (
	"fmt"
	"io"
	"net"
	"os"
	"time"

	"golang.org/x/crypto/ssh"
	"golang.org/x/term"
)

type Cli struct {
	IP         string       //IP地址
	Username   string       //用户名
	Password   string       //密码
	Port       int          //端口号
	client     *ssh.Client  //ssh客户端
	sftp       *sftp.Client // sftp客户端
	LastResult string       //最近一次Run的结果
}

//创建命令行对象
//@param ip IP地址
//@param username 用户名
//@param password 密码
//@param port 端口号,默认22
func New(ip string, username string, password string, port ...int) *Cli {
	cli := new(Cli)
	cli.IP = ip
	cli.Username = username
	cli.Password = password
	if len(port) <= 0 {
		cli.Port = 22
	} else {
		cli.Port = port[0]
	}

	if cli.client == nil {
		cli.connect()
	}

	return cli
}

//执行shell
//@param shell shell脚本命令
func (c Cli) Run(shell string) (string, error) {

	if c.client == nil {
		return "", errors.New("ssh cli not connect")
	}
	session, err := c.client.NewSession()
	if err != nil {
		return "", err
	}
	defer session.Close()
	buf, err := session.CombinedOutput(shell)

	c.LastResult = string(buf)
	return c.LastResult, err
}

//连接
func (c *Cli) connect() error {
	config := ssh.ClientConfig{
		User: c.Username,
		Auth: []ssh.AuthMethod{ssh.Password(c.Password)},
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
		Timeout: 10 * time.Second,
	}
	addr := fmt.Sprintf("%s:%d", c.IP, c.Port)
	sshClient, err := ssh.Dial("tcp", addr, &config)
	if err != nil {
		return err
	}
	c.client = sshClient
	return nil
}

//执行带交互的命令
func (c *Cli) RunTerminal(command string, stdout, stderr io.Writer) error {

	if c.client == nil {
		return errors.New("ssh cli not connect")
	}
	session, err := c.client.NewSession()
	if err != nil {
		return err
	}
	defer session.Close()

	fd := int(os.Stdin.Fd())
	oldState, err := terminal.MakeRaw(fd)
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(fd, oldState)

	session.Stdout = stdout
	session.Stderr = stderr
	session.Stdin = os.Stdin

	termWidth, termHeight, err := terminal.GetSize(fd)
	if err != nil {
		panic(err)
	}
	// Set up terminal modes
	modes := ssh.TerminalModes{
		ssh.ECHO:          1,     // enable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	// Request pseudo terminal
	if err := session.RequestPty("xterm-256color", termHeight, termWidth, modes); err != nil {
		return err
	}

	session.Run(command)
	return nil
}

// 创建sftp
func (c *Cli) NewSftpCli() error {

	if c.client == nil {
		return errors.New("ssh cli not connect")
	}
	if sftp, err := sftp.NewClient(c.client); err != nil { //创建客户端
		return err
	} else {
		c.sftp = sftp
	}

	return nil
}

// 下载文件
func (c *Cli) Download(filepath, outpath string) error {

	if c.sftp == nil {
		return errors.New("sftp cli not connect")
	}
	cloudFile, err := c.sftp.Open(filepath)
	if err != nil {
		return err
	}

	defer cloudFile.Close()

	// 本地文件创建
	localFile, err := os.Create(outpath)
	if err != nil {
		return err
	}

	defer localFile.Close()
	_, err = io.Copy(localFile, cloudFile)

	return err
}



调用测试

func main() {
	var (
		host    = "127.0.0.1"
		svcAcc  = "root"
		svsPwd  = "123124123"
		command = "docker exec -it my-data mysqldump -h127.0.0.1 -uroot -p123321 -P3306 test > /root/backup/backup.sql"
		port    = 22
	)

	cli := service.SshNewCli(host, svcAcc, svsPwd, port)
	cli.RunTerminal(command, os.Stdout, os.Stdin)
    
    cli.NewSftpCli()
	cli.Download("/root/backup/backup.sql", "/Users/xxx/codes/backup/backup.sql")
}