这篇文章主要介绍了如何使用golang封装ssh用于在远程主机上执行命令,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让...

python中可以paramiko实现在远程主机上执行命令,上传和下载文件,用go也可以封装一个,在go中用ssh就sftp包可以实现,实现了下面的功能

  • 在远程主机执行命令返回结果、返回值

  • 上传和下载文件远程主机上,以及传输了多少个字节

认证方式

  • 如果指定了密码,那么采用用户+密码的方式认证,否则采用用户+秘钥的方式

    • 如果没有指定用户,则默认使用当前的用户

  • 如果没有指定密码,将采用用户+秘钥方式,默认取~/.ssh/id_rsa文件私钥文件获取秘钥
    和paramik类似

直接上代码

packagemainimport("errors""fmt""github.com/pkg/sftp""golang.org/x/crypto/ssh""io""io/ioutil""log""os""os/user""time")var(DefaultSShTcpTimeout=15*time.Second//与ssh建立连接的默认时间,自己设置一个就行)//错误定义var(InvalidHostName=errors.New("invalidparameters:hostnameisempty")InvalidPort=errors.New("invalidparameters:portmustberange0~65535"))//返回当前用户名funcgetCurrentUser()string{user,_:=user.Current()returnuser.Username}//存放上传或下载的信息typeTransferInfostruct{Kindstring//upload或downloadLocalstring//本地路径Dststring//目标路径TransferByteint64//传输的字节数(byte)}func(t*TransferInfo)String()string{returnfmt.Sprintf(`TransforInfo(Kind:"%s",Local:"%s",Dst:"%s",TransferByte:%d)`,t.Kind,t.Local,t.Dst,t.TransferByte)}//存放执行结果的结构体信息typeExecInfostruct{CmdstringOutput[]byteExitCodeint}func(e*ExecInfo)OutputString()string{returnstring(e.Output)}func(e*ExecInfo)String()string{returnfmt.Sprintf(`ExecInfo(cmd:"%s",exitcode:%d)`,e.Cmd,e.ExitCode)}typeAuthConfigstruct{*ssh.ClientConfigUserstringPasswordstringKeyFilestringTimeouttime.Duration}func(a*AuthConfig)setDefault(){ifa.User==""{a.User=getCurrentUser()}ifa.KeyFile==""{userHome,_:=os.UserHomeDir()a.KeyFile=fmt.Sprintf("%s/.ssh/id_rsa",userHome)}ifa.Timeout==0{a.Timeout=DefaultSShTcpTimeout}}func(a*AuthConfig)SetAuthMethod()(ssh.AuthMethod,error){a.setDefault()ifa.Password!=""{returnssh.Password(a.Password),nil}data,err:=ioutil.ReadFile(a.KeyFile)iferr!=nil{returnnil,err}singer,err:=ssh.ParsePrivateKey(data)iferr!=nil{returnnil,err}returnssh.PublicKeys(singer),nil}func(a*AuthConfig)ApplyConfig()error{authMethod,err:=a.SetAuthMethod()iferr!=nil{returnerr}a.ClientConfig=&ssh.ClientConfig{User:a.User,Auth:[]ssh.AuthMethod{authMethod},HostKeyCallback:ssh.InsecureIgnoreHostKey(),Timeout:a.Timeout,}returnnil}//存放连接的结构体typeconnstruct{client*ssh.ClientsftpClient*sftp.Client}func(c*conn)Close(){ifc.sftpClient!=nil{c.sftpClient.Close()c.sftpClient=nil}ifc.client!=nil{c.client.Close()c.client=nil}}//SSHClient结构体typeSSHClientstruct{connHostNamestringPortintAuthConfigAuthConfig}//设置默认端口信息func(s*SSHClient)setDefaultValue(){ifs.Port==0{s.Port=22}}//与远程主机连接func(s*SSHClient)Connect()error{ifs.client!=nil{log.Println("AlreadyLogin")returnnil}iferr:=s.AuthConfig.ApplyConfig();err!=nil{returnerr}s.setDefaultValue()addr:=fmt.Sprintf("%s:%d",s.HostName,s.Port)varerrerrors.client,err=ssh.Dial("tcp",addr,s.AuthConfig.ClientConfig)iferr!=nil{returnerr}returnnil}//一个session只能执行一次命令,也就是说不能在同一个session执行多次s.session.CombinedOutput//如果想执行多次,需要每条为每个命令创建一个session(这里是这样做)func(s*SSHClient)Exec(cmdstring)(*ExecInfo,error){session,err:=s.client.NewSession()iferr!=nil{returnnil,err}defersession.Close()output,err:=session.CombinedOutput(cmd)varexitcodeintiferr!=nil{//断言转成具体实现类型,获取返回值exitcode=err.(*ssh.ExitError).ExitStatus()}return&ExecInfo{Cmd:cmd,Output:output,ExitCode:exitcode,},nil}//将本地文件上传到远程主机上func(s*SSHClient)Upload(localPathstring,dstPathstring)(*TransferInfo,error){transferInfo:=&TransferInfo{Kind:"upload",Local:localPath,Dst:dstPath,TransferByte:0}varerrerror//如果sftp客户端没有打开,就打开,为了复用ifs.sftpClient==nil{ifs.sftpClient,err=sftp.NewClient(s.client);err!=nil{returntransferInfo,err}}localFileObj,err:=os.Open(localPath)iferr!=nil{returntransferInfo,err}deferlocalFileObj.Close()dstFileObj,err:=s.sftpClient.Create(dstPath)iferr!=nil{returntransferInfo,err}deferdstFileObj.Close()written,err:=io.Copy(dstFileObj,localFileObj)iferr!=nil{returntransferInfo,err}transferInfo.TransferByte=writtenreturntransferInfo,nil}//从远程主机上下载文件到本地func(s*SSHClient)Download(dstPathstring,localPathstring)(*TransferInfo,error){transferInfo:=&TransferInfo{Kind:"download",Local:localPath,Dst:dstPath,TransferByte:0}varerrerrorifs.sftpClient==nil{ifs.sftpClient,err=sftp.NewClient(s.client);err!=nil{returntransferInfo,err}}//defers.sftpClient.Close()localFileObj,err:=os.Create(localPath)iferr!=nil{returntransferInfo,err}deferlocalFileObj.Close()dstFileObj,err:=s.sftpClient.Open(dstPath)iferr!=nil{returntransferInfo,err}deferdstFileObj.Close()written,err:=io.Copy(localFileObj,dstFileObj)iferr!=nil{returntransferInfo,err}transferInfo.TransferByte=writtenreturntransferInfo,nil}//SSHclient的构造方法funcNewSSHClient(hostnamestring,portint,authConfigAuthConfig)(*SSHClient,error){switch{casehostname=="":returnnil,InvalidHostNamecaseport>65535||port<0:returnnil,InvalidPort}sshClient:=&SSHClient{HostName:hostname,Port:port,AuthConfig:authConfig}err:=sshClient.Connect()iferr!=nil{returnnil,err}returnsshClient,nil}funcmain(){//测试sshClient,err:=NewSSHClient("172.16.0.178",22,AuthConfig{User:"root"})iferr!=nil{fmt.Println(err)return}defersshClient.Close()//第一次执行命令execinfo,err:=sshClient.Exec("ls-l")fmt.Println(execinfo.OutputString(),err)//第二次执行命令out1,exitcode2:=sshClient.Exec("ifconfig-a")fmt.Println(string(out1),exitcode2)//上传文件transInfoUpload,err:=sshClient.Upload("/tmp/passwd","/tmp/password_upload")fmt.Println(transInfoUpload,err)//下载文件transInfoDownload,err:=sshClient.Download("/etc/passwd","/tmp/passwd_download")fmt.Println(transInfoDownload,err)}