背景
当我执行一些shell命令时, 经常会要求我们输入yes 或者 用户名/密码;如果我们想要编写一个自动化运行的shell脚本,希望程序自动帮助我们填写,原生的shell是不支持的, 需要单独安装expect; 可以参考:expect自动输入密码、命令
由于目前多用go语言开发,而且go也很容易打包成可执行文件,操作起来非常方便,并且可以结合shell和本身的语法完成更加复杂的业务, 所以本文主要介绍通过go来实现交互执行shell命令;
gexpect
以ssh 为例, 主要是通过 github.com/ThomasRooney/gexpect 来实现
gossh.go
package main
import (
"log"
"time"
"github.com/ThomasRooney/gexpect"
)
func main() {
// cmd := "sh ./test.sh"
cmd := `ssh username@server_ip`
// cmd := `ssh username@server_ip "ls"` // 登录后执行ls 命令
pwd := "yourpassword"
child, err := gexpect.Spawn(cmd)
if err != nil {
log.Fatal("Spawn cmd error ", err)
}
if err := child.ExpectTimeout("password:", 10*time.Second); err != nil {
log.Fatal("Expect timieout error ", err)
}
if err := child.SendLine(pwd); err != nil {
log.Fatal("SendLine password error ", err)
}
// child.Expect("password:")
// child.SendLine(pwd)
child.Interact()
child.Close()
}
os/exec
type Cmd struct {
Path string //运行命令的路径,绝对路径或者相对路径
Args []string // 命令参数
Env []string //进程环境,如果环境为空,则使用当前进程的环境
Dir string //指定command的工作目录,如果dir为空,则comman在调用进程所在当前目录中运行
Stdin io.Reader //标准输入,如果stdin是nil的话,进程从null device中读取(os.DevNull),stdin也可以时一个文件,否则的话则在运行过程中再开一个goroutine去
//读取标准输入
Stdout io.Writer //标准输出
Stderr io.Writer //错误输出,如果这两个(Stdout和Stderr)为空的话,则command运行时将响应的文件描述符连接到os.DevNull
ExtraFiles []*os.File
SysProcAttr *syscall.SysProcAttr
Process *os.Process //Process是底层进程,只启动一次
ProcessState *os.ProcessState //ProcessState包含一个退出进程的信息,当进程调用Wait或者Run时便会产生该信息.
}
func ExecTest
func ExecTest() {
cmd := exec.Command("sh", "./test.sh")
cmd.Stdin = strings.NewReader("127.0.0.1\n 3306\n") //多次输入
var out bytes.Buffer
cmd.Stdout = &out //输出
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Printf(out.String())
}
test.sh
#!/bin/bash
read -p "ip:" IP
echo "ip:"$IP
read -p "port:" PORT
echo "====="
echo $IP":"$PORT
echo "====="
官方ssh工具
【example】
package main
import (
"bytes"
"fmt"
"golang.org/x/crypto/ssh"
"log"
)
func main() {
var hostKey ssh.PublicKey
// An SSH client is represented with a ClientConn.
//
// To authenticate with the remote server you must pass at least one
// implementation of AuthMethod via the Auth field in ClientConfig,
// and provide a HostKeyCallback.
config := &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{
ssh.Password("yourpassword"),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
}
client, err := ssh.Dial("tcp", "yourserver.com:22", config)
if err != nil {
log.Fatal("Failed to dial: ", err)
}
defer client.Close()
// Each ClientConn can support multiple interactive sessions,
// represented by a Session.
session, err := client.NewSession()
if err != nil {
log.Fatal("Failed to create session: ", err)
}
defer session.Close()
// Once a Session is created, you can execute a single command on
// the remote side using the Run method.
var b bytes.Buffer
session.Stdout = &b
if err := session.Run("/usr/bin/whoami"); err != nil {
log.Fatal("Failed to run: " + err.Error())
}
fmt.Println(b.String())
}
推荐阅读:
https://www.cnblogs.com/chenqionghe/p/8267326.html
另一个封装:
https://github.com/scottkiss/gosshtool
http://www.cocosk.com/articles/2016/6/18/go-ssh-client-1.html