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")
}