Golang ssh – 如何在同一个会话上运行多个命令?

huangapple go评论78阅读模式
英文:

Golang ssh - how to run multiple commands on the same session?

问题

我正在尝试通过ssh运行多个命令,但似乎Session.Run只允许每个会话运行一个命令(除非我错了)。我想知道如何绕过这个限制并重用会话或发送一系列命令。
原因是我需要在同一个会话中运行sudo su命令,然后再运行下一个命令(sh /usr/bin/myscript.sh)。

英文:

I'm trying to run multiple commands through ssh but seems that Session.Run allows only one command per session ( unless I'm wrong). I'm wondering how can I bypass this limitation and reuse the session or send a sequence of commands.
The reason is that I need to run sudo su within the same session with the next command ( sh /usr/bin/myscript.sh )

答案1

得分: 14

Session.Shell 允许通过 session.StdinPipe() 将多个命令传递进去以运行。

请注意,使用这种方法会使您的生活变得更加复杂;您不再有一个一次性的函数调用来运行命令并在完成后收集输出,而是需要管理输入缓冲区(不要忘记在命令的末尾加上 \n),等待实际从 SSH 服务器返回输出,然后适当处理该输出(如果您有多个正在执行的命令并且想知道哪个输出属于哪个输入,您需要有一个计划来解决这个问题)。

stdinBuf, _ := session.StdinPipe()
err := session.Shell()
stdinBuf.Write([]byte("cd /\n"))
// 命令已发送到设备,但尚未收到输出。
// 注意,您可以立即发送更多的命令。
stdinBuf.Write([]byte("ls\n"))
// 然后您将等待响应,并监视 stdout 缓冲区以获取输出。
英文:

Session.Shell allows for more than one command to be run, by passing your commands in via session.StdinPipe().

Be aware that using this approach will make your life more complicated; instead of having a one-shot function call that runs the command and collects the output once it's complete, you'll need to manage your input buffer (don't forget a \n at the end of a command), wait for output to actually come back from the SSH server, then deal with that output appropriately (if you had multiple commands in flight and want to know what output belongs to what input, you'll need to have a plan to figure that out).

stdinBuf, _ := session.StdinPipe()
err := session.Shell()
stdinBuf.Write([]byte("cd /\n"))
// The command has been sent to the device, but you haven't gotten output back yet.
// Not that you can't send more commands immediately.
stdinBuf.Write([]byte("ls\n"))
// Then you'll want to wait for the response, and watch the stdout buffer for output.

答案2

得分: 11

对于你的特定问题,你可以轻松地运行sudo /path/to/script.sh,但让我感到震惊的是没有一种简单的方法在同一个会话中运行多个命令,所以我想出了一个有点hack,效果可能有所不同:

func MuxShell(w io.Writer, r io.Reader) (chan<- string, <-chan string) {
	in := make(chan string, 1)
	out := make(chan string, 1)
	var wg sync.WaitGroup
	wg.Add(1) //for the shell itself
	go func() {
		for cmd := range in {
			wg.Add(1)
			w.Write([]byte(cmd + "\n"))
			wg.Wait()
		}
	}()
	go func() {
		var (
			buf [65 * 1024]byte
			t   int
		)
		for {
			n, err := r.Read(buf[t:])
			if err != nil {
				close(in)
				close(out)
				return
			}
			t += n
			if buf[t-2] == '$' { //assuming the $PS1 == 'sh-4.3$ '
				out <- string(buf[:t])
				t = 0
				wg.Done()
			}
		}
	}()
	return in, out
}

func main() {
	config := &ssh.ClientConfig{
		User: "kf5",
		Auth: []ssh.AuthMethod{
			ssh.Password("kf5"),
		},
	}
	client, err := ssh.Dial("tcp", "127.0.0.1:22", config)
	if err != nil {
		panic(err)
	}

	defer client.Close()
	session, err := client.NewSession()

	if err != nil {
		log.Fatalf("unable to create session: %s", err)
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		log.Fatal(err)
	}

	w, err := session.StdinPipe()
	if err != nil {
		panic(err)
	}
	r, err := session.StdoutPipe()
	if err != nil {
		panic(err)
	}
	in, out := MuxShell(w, r)
	if err := session.Start("/bin/sh"); err != nil {
		log.Fatal(err)
	}
	<-out //ignore the shell output
	in <- "ls -lhav"
	fmt.Printf("ls output: %s\n", <-out)

	in <- "whoami"
	fmt.Printf("whoami: %s\n", <-out)

	in <- "exit"
	session.Wait()
}

如果你的shell提示符不以$ ($后面跟一个空格)结尾,这段代码会造成死锁,所以这只是一个hack。

英文:

While for your specific problem, you can easily run sudo /path/to/script.sh, it shock me that there wasn't a simple way to run multiple commands on the same session, so I came up with a bit of a hack, YMMV:

func MuxShell(w io.Writer, r io.Reader) (chan&lt;- string, &lt;-chan string) {
in := make(chan string, 1)
out := make(chan string, 1)
var wg sync.WaitGroup
wg.Add(1) //for the shell itself
go func() {
for cmd := range in {
wg.Add(1)
w.Write([]byte(cmd + &quot;\n&quot;))
wg.Wait()
}
}()
go func() {
var (
buf [65 * 1024]byte
t   int
)
for {
n, err := r.Read(buf[t:])
if err != nil {
close(in)
close(out)
return
}
t += n
if buf[t-2] == &#39;$&#39; { //assuming the $PS1 == &#39;sh-4.3$ &#39;
out &lt;- string(buf[:t])
t = 0
wg.Done()
}
}
}()
return in, out
}
func main() {
config := &amp;ssh.ClientConfig{
User: &quot;kf5&quot;,
Auth: []ssh.AuthMethod{
ssh.Password(&quot;kf5&quot;),
},
}
client, err := ssh.Dial(&quot;tcp&quot;, &quot;127.0.0.1:22&quot;, config)
if err != nil {
panic(err)
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
log.Fatalf(&quot;unable to create session: %s&quot;, err)
}
defer session.Close()
modes := ssh.TerminalModes{
ssh.ECHO:          0,     // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty(&quot;xterm&quot;, 80, 40, modes); err != nil {
log.Fatal(err)
}
w, err := session.StdinPipe()
if err != nil {
panic(err)
}
r, err := session.StdoutPipe()
if err != nil {
panic(err)
}
in, out := MuxShell(w, r)
if err := session.Start(&quot;/bin/sh&quot;); err != nil {
log.Fatal(err)
}
&lt;-out //ignore the shell output
in &lt;- &quot;ls -lhav&quot;
fmt.Printf(&quot;ls output: %s\n&quot;, &lt;-out)
in &lt;- &quot;whoami&quot;
fmt.Printf(&quot;whoami: %s\n&quot;, &lt;-out)
in &lt;- &quot;exit&quot;
session.Wait()
}

If your shell prompt doesn't end with $ ($ followed by a space), this will deadlock, hence why it's a hack.

答案3

得分: 9

NewSession是一个连接的方法。您不需要每次都创建一个新的连接。一个Session似乎是这个库中称为客户端通道的东西,许多通道在一个单独的连接中进行多路复用。因此:

func executeCmd(cmd []string, hostname string, config *ssh.ClientConfig) string {
    conn, err := ssh.Dial("tcp", hostname+":8022", config)

    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    var stdoutBuf bytes.Buffer

    for _, command := range cmd {

        session, err := conn.NewSession()

        if err != nil {
            log.Fatal(err)
        }
        defer session.Close()

        session.Stdout = &stdoutBuf
        session.Run(command)
    }

    return hostname + ": " + stdoutBuf.String()
}

所以您打开一个新的会话(通道),并在现有的ssh连接中运行命令,但每次都使用一个新的会话(通道)。

英文:

NewSession is a method of a connection. You don't need to create a new connection each time. A Session seems to be what this library calls a channel for the client, and many channels are multiplexed in a single connection. Hence:

func executeCmd(cmd []string, hostname string, config *ssh.ClientConfig) string {
conn, err := ssh.Dial(&quot;tcp&quot;, hostname+&quot;:8022&quot;, config)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
var stdoutBuf bytes.Buffer
for _, command := range cmd {
session, err := conn.NewSession()
if err != nil {
log.Fatal(err)
}
defer session.Close()
session.Stdout = &amp;stdoutBuf
session.Run(command)
}
return hostname + &quot;: &quot; + stdoutBuf.String()
}

So you open a new session(channel) and you run command within the existing ssh connection but with a new session(channel) each time.

答案4

得分: 6

你可以使用一个小技巧:

sh -c 'cmd1&&cmd2&&cmd3&&cmd4&&etc..'

这是一个单一的命令,实际的命令作为参数传递给 shell 来执行。这就是 Docker 处理多个命令的方式。

英文:

You can use a small trick:

sh -c &#39;cmd1&amp;&amp;cmd2&amp;&amp;cmd3&amp;&amp;cmd4&amp;&amp;etc..&#39;

This is a single command, the actual commands are passed as argument to the shell which will execute them. This is how Docker handles multiple commands.

答案5

得分: 4

这是一个用于远程连接主机并执行命令的Go语言代码。它使用了"golang.org/x/crypto/ssh"包来实现SSH连接。代码中定义了一个MachineDetails结构体,包含了用户名、密码、主机名和端口号等连接参数。主函数main()中创建了一个MachineDetails实例h1,并定义了要执行的命令列表commands。然后调用connectHost()函数来连接主机并执行命令。connectHost()函数中首先配置SSH客户端,然后连接主机并创建会话。接着设置标准输入输出和错误输出,通过标准输入管道发送命令,并等待会话结束。整个过程使用了错误处理来处理可能出现的错误。

英文:

This works for me.

package main
import (
&quot;fmt&quot;
&quot;golang.org/x/crypto/ssh&quot;
// &quot;io&quot;
&quot;log&quot;
&quot;os&quot;
// Uncomment to store output in variable
//&quot;bytes&quot;
)
type MachineDetails struct {
username, password, hostname, port string
}
func main() {
h1 := MachineDetails{&quot;root&quot;, &quot;xxxxx&quot;, &quot;x.x.x.x&quot;, &quot;22&quot;}
// Uncomment to store output in variable
//var b bytes.Buffer
//sess.Stdout = &amp;amp;b
//sess.Stderr = &amp;amp;b
commands := []string{
&quot;pwd&quot;,
&quot;whoami&quot;,
&quot;echo &#39;bye&#39;&quot;,
&quot;exit&quot;,
}
connectHost(h1, commands)
// Uncomment to store in variable
//fmt.Println(b.String())
}
func connectHost(hostParams MachineDetails, commands []string) {
// SSH client config
config := &amp;ssh.ClientConfig{
User: hostParams.username,
Auth: []ssh.AuthMethod{
ssh.Password(hostParams.password),
},
// Non-production only
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// Connect to host
client, err := ssh.Dial(&quot;tcp&quot;, hostParams.hostname+&quot;:&quot;+hostParams.port, config)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Create sesssion
sess, err := client.NewSession()
if err != nil {
log.Fatal(&quot;Failed to create session: &quot;, err)
}
defer sess.Close()
// Enable system stdout
// Comment these if you uncomment to store in variable
sess.Stdout = os.Stdout
sess.Stderr = os.Stderr
// StdinPipe for commands
stdin, err := sess.StdinPipe()
if err != nil {
log.Fatal(err)
}
// Start remote shell
err = sess.Shell()
if err != nil {
log.Fatal(err)
}
// send the commands
for _, cmd := range commands {
_, err = fmt.Fprintf(stdin, &quot;%s\n&quot;, cmd)
if err != nil {
log.Fatal(err)
}
}
// Wait for sess to finish
err = sess.Wait()
if err != nil {
log.Fatal(err)
}
// return sess, stdin, err
}
func createSession() {
}

答案6

得分: 0

我很喜欢OneOfOne的答案,它给了我一个更通用的解决方案,可以匹配读取字节的尾部并中断阻塞读取(也不需要为阻塞读取和写入额外创建两个线程)。已知的限制是(与原始解决方案相同),如果匹配的字符串出现在64 * 1024字节之后,那么这段代码将会一直循环。

package main

import (
	"fmt"
	"golang.org/x/crypto/ssh"
	"io"
	"log"
)

var escapePrompt = []byte{'$', ' '}

func main() {
	config := &ssh.ClientConfig{
		User: "dummy",
		Auth: []ssh.AuthMethod{
			ssh.Password("dummy"),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}
	client, err := ssh.Dial("tcp", "127.0.0.1:22", config)
	if err != nil {
		panic(err)
	}

	defer client.Close()
	session, err := client.NewSession()

	if err != nil {
		log.Fatalf("unable to create session: %s", err)
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          0,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
		log.Fatal(err)
	}

	w, err := session.StdinPipe()
	if err != nil {
		panic(err)
	}
	r, err := session.StdoutPipe()
	if err != nil {
		panic(err)
	}
	if err := session.Start("/bin/sh"); err != nil {
		log.Fatal(err)
	}
	readUntil(r, escapePrompt) //忽略shell输出

	write(w, "ls -lhav")

	out, err := readUntil(r, escapePrompt)

	fmt.Printf("ls output: %s\n", *out)

	write(w, "whoami")

	out, err = readUntil(r, escapePrompt)

	fmt.Printf("whoami: %s\n", *out)

	write(w, "exit")

	session.Wait()
}

func write(w io.WriteCloser, command string) error {
	_, err := w.Write([]byte(command + "\n"))
	return err
}

func readUntil(r io.Reader, matchingByte []byte) (*string, error) {
	var buf [64 * 1024]byte
	var t int
	for {
		n, err := r.Read(buf[t:])
		if err != nil {
			return nil, err
		}
		t += n
		if isMatch(buf[:t], t, matchingByte) {
			stringResult := string(buf[:t])
			return &stringResult, nil
		}
	}
}

func isMatch(bytes []byte, t int, matchingBytes []byte) bool {
	if t >= len(matchingBytes) {
		for i := 0; i < len(matchingBytes); i++ {
			if bytes[t-len(matchingBytes)+i] != matchingBytes[i] {
				return false
			}
		}
		return true
	}
	return false
}

[1]: https://play.golang.org/p/4RmeJ7dOpOV
英文:

Really liked OneOfOne's answer which inspired me with a more generalized solution to taken a variable that could match the tail of the read bytes and break the blocking read (also no need to fork two extra threads for blocking read and writes). The known limitation is (as in the original solution) if the matching string comes after 64 * 1024 bytes, then this code will spin forever.

package main
import (
&quot;fmt&quot;
&quot;golang.org/x/crypto/ssh&quot;
&quot;io&quot;
&quot;log&quot;
)
var escapePrompt = []byte{&#39;$&#39;, &#39; &#39;}
func main() {
config := &amp;ssh.ClientConfig{
User: &quot;dummy&quot;,
Auth: []ssh.AuthMethod{
ssh.Password(&quot;dummy&quot;),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
client, err := ssh.Dial(&quot;tcp&quot;, &quot;127.0.0.1:22&quot;, config)
if err != nil {
panic(err)
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
log.Fatalf(&quot;unable to create session: %s&quot;, err)
}
defer session.Close()
modes := ssh.TerminalModes{
ssh.ECHO:          0,     // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty(&quot;xterm&quot;, 80, 40, modes); err != nil {
log.Fatal(err)
}
w, err := session.StdinPipe()
if err != nil {
panic(err)
}
r, err := session.StdoutPipe()
if err != nil {
panic(err)
}
if err := session.Start(&quot;/bin/sh&quot;); err != nil {
log.Fatal(err)
}
readUntil(r, escapePrompt) //ignore the shell output
write(w, &quot;ls -lhav&quot;)
out, err := readUntil(r, escapePrompt)
fmt.Printf(&quot;ls output: %s\n&quot;, *out)
write(w, &quot;whoami&quot;)
out, err = readUntil(r, escapePrompt)
fmt.Printf(&quot;whoami: %s\n&quot;, *out)
write(w, &quot;exit&quot;)
session.Wait()
}
func write(w io.WriteCloser, command string) error {
_, err := w.Write([]byte(command + &quot;\n&quot;))
return err
}
func readUntil(r io.Reader, matchingByte []byte) (*string, error) {
var buf [64 * 1024]byte
var t int
for {
n, err := r.Read(buf[t:])
if err != nil {
return nil, err
}
t += n
if isMatch(buf[:t], t, matchingByte) {
stringResult := string(buf[:t])
return &amp;stringResult, nil
}
}
}
func isMatch(bytes []byte, t int, matchingBytes []byte) bool {
if t &gt;= len(matchingBytes) {
for i := 0; i &lt; len(matchingBytes); i++ {
if bytes[t - len(matchingBytes) + i] != matchingBytes[i] {
return false
}
}
return true
}
return false
}

答案7

得分: 0

从这里获得灵感

我花了几天时间,这个答案激发了我尝试使用sdtin运行多个命令,最终成功了。我想说我完全不懂golang,所以可能有些多余,但代码是有效的。

if _, err := w.Write([]byte("sys\r")); err != nil {
    panic("Failed to run: " + err.Error())
}
if _, err := w.Write([]byte("wlan\r")); err != nil {
    panic("Failed to run: " + err.Error())
}
if _, err := w.Write([]byte("ap-id 2099\r")); err != nil {
    panic("Failed to run: " + err.Error())
}

if _, err := w.Write([]byte("ap-group xuebao-free\r")); err != nil {
    panic("Failed to run: " + err.Error())
}
if _, err := w.Write([]byte("y\r")); err != nil {
    panic("Failed to run: " + err.Error())
}

它的功能与终端操作相同。

以下是完整的代码:

/* switch ssh
 */
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
	"sync"
)

import (
	"golang.org/x/crypto/ssh"
)

func main() {
	//go run ./testConfig.go --username="aaa" --passwd='aaa' --ip_port="192.168.6.87" --cmd='display version'
	username := flag.String("username", "aaa", "username")
	passwd := flag.String("passwd", "aaa", "password")
	ip_port := flag.String("ip_port", "1.1.1.1:22", "ip and port")
	cmdstring := flag.String("cmd", "display arp statistics all", "cmdstring")
	flag.Parse()
	fmt.Println("username:", *username)
	fmt.Println("passwd:", *passwd)
	fmt.Println("ip_port:", *ip_port)
	fmt.Println("cmdstring:", *cmdstring)
	config := &ssh.ClientConfig{
		User: *username,
		Auth: []ssh.AuthMethod{
			ssh.Password(*passwd),
		},
		Config: ssh.Config{
			Ciphers: []string{"aes128-cbc", "aes128-ctr"},
		},
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	}
	// config.Config.Ciphers = append(config.Config.Ciphers, "aes128-cbc")
	clinet, err := ssh.Dial("tcp", *ip_port, config)
	checkError(err, "connet "+*ip_port)

	session, err := clinet.NewSession()
	defer session.Close()
	checkError(err, "creae shell")

	modes := ssh.TerminalModes{
		ssh.ECHO:          1,     // disable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	if err := session.RequestPty("vt100", 80, 40, modes); err != nil {
		log.Fatal(err)
	}

	w, err := session.StdinPipe()
	if err != nil {
		panic(err)
	}
	r, err := session.StdoutPipe()
	if err != nil {
		panic(err)
	}
	e, err := session.StderrPipe()
	if err != nil {
		panic(err)
	}

	in, out := MuxShell(w, r, e)
	if err := session.Shell(); err != nil {
		log.Fatal(err)
	}
	<-out //ignore the shell output
	in <- *cmdstring
	fmt.Printf("%s\n", <-out)


	if _, err := w.Write([]byte("sys\r")); err != nil {
        panic("Failed to run: " + err.Error())
    }
if _, err := w.Write([]byte("wlan\r")); err != nil {
        panic("Failed to run: " + err.Error())
    }
if _, err := w.Write([]byte("ap-id 2099\r")); err != nil {
        panic("Failed to run: " + err.Error())
    }

if _, err := w.Write([]byte("ap-group xuebao-free\r")); err != nil {
        panic("Failed to run: " + err.Error())
    }
if _, err := w.Write([]byte("y\r")); err != nil {
        panic("Failed to run: " + err.Error())
    }

	in <- "quit"
	_ = <-out
	session.Wait()
}

func checkError(err error, info string) {
	if err != nil {
		fmt.Printf("%s. error: %s\n", info, err)
		os.Exit(1)
	}
}

func MuxShell(w io.Writer, r, e io.Reader) (chan<- string, <-chan string) {
	in := make(chan string, 5)
	out := make(chan string, 5)
	var wg sync.WaitGroup
	wg.Add(1) //for the shell itself
	go func() {
		for cmd := range in {
			wg.Add(1)
			w.Write([]byte(cmd + "\n"))
			wg.Wait()
		}
	}()

	go func() {
		var (
			buf [1024 * 1024]byte
			t   int
		)
		for {
			n, err := r.Read(buf[t:])
			if err != nil {
				fmt.Println(err.Error())
				close(in)
				close(out)
				return
			}
			t += n
			result := string(buf[:t])
			if strings.Contains(string(buf[t-n:t]), "More") {
				w.Write([]byte("\n"))
			}
			if strings.Contains(result, "username:") ||
				strings.Contains(result, "password:") ||
				strings.Contains(result, ">") {
				out <- string(buf[:t])
				t = 0
				wg.Done()
			}
		}
	}()
	return in, out
}
英文:

get inspiration from this

i spent several days and that answer inspires me to try about using sdtin to run multiple commands, finally succeed. and i want to say i dont know golang at all , hence it may be redundant ,but the code works.

if _, err := w.Write([]byte(&quot;sys\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
if _, err := w.Write([]byte(&quot;wlan\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
if _, err := w.Write([]byte(&quot;ap-id 2099\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
if _, err := w.Write([]byte(&quot;ap-group xuebao-free\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
if _, err := w.Write([]byte(&quot;y\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}

its function is the same asterminal operation

here is the whole code:

/* switch ssh
*/
package main
import (
&quot;flag&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;log&quot;
&quot;net&quot;
&quot;os&quot;
&quot;strings&quot;
&quot;sync&quot;
)
import (
&quot;golang.org/x/crypto/ssh&quot;
)
func main() {
//go run ./testConfig.go --username=&quot;aaa&quot; --passwd=&#39;aaa&#39; --ip_port=&quot;192.168.6.87&quot; --cmd=&#39;display version&#39;
username := flag.String(&quot;username&quot;, &quot;aaa&quot;, &quot;username&quot;)
passwd := flag.String(&quot;passwd&quot;, &quot;aaa&quot;, &quot;password&quot;)
ip_port := flag.String(&quot;ip_port&quot;, &quot;1.1.1.1:22&quot;, &quot;ip and port&quot;)
cmdstring := flag.String(&quot;cmd&quot;, &quot;display arp statistics all&quot;, &quot;cmdstring&quot;)
flag.Parse()
fmt.Println(&quot;username:&quot;, *username)
fmt.Println(&quot;passwd:&quot;, *passwd)
fmt.Println(&quot;ip_port:&quot;, *ip_port)
fmt.Println(&quot;cmdstring:&quot;, *cmdstring)
config := &amp;ssh.ClientConfig{
User: *username,
Auth: []ssh.AuthMethod{
ssh.Password(*passwd),
},
Config: ssh.Config{
Ciphers: []string{&quot;aes128-cbc&quot;, &quot;aes128-ctr&quot;},
},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
// config.Config.Ciphers = append(config.Config.Ciphers, &quot;aes128-cbc&quot;)
clinet, err := ssh.Dial(&quot;tcp&quot;, *ip_port, config)
checkError(err, &quot;connet &quot;+*ip_port)
session, err := clinet.NewSession()
defer session.Close()
checkError(err, &quot;creae shell&quot;)
modes := ssh.TerminalModes{
ssh.ECHO:          1,     // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty(&quot;vt100&quot;, 80, 40, modes); err != nil {
log.Fatal(err)
}
w, err := session.StdinPipe()
if err != nil {
panic(err)
}
r, err := session.StdoutPipe()
if err != nil {
panic(err)
}
e, err := session.StderrPipe()
if err != nil {
panic(err)
}
in, out := MuxShell(w, r, e)
if err := session.Shell(); err != nil {
log.Fatal(err)
}
&lt;-out //ignore the shell output
in &lt;- *cmdstring
fmt.Printf(&quot;%s\n&quot;, &lt;-out)
if _, err := w.Write([]byte(&quot;sys\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
if _, err := w.Write([]byte(&quot;wlan\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
if _, err := w.Write([]byte(&quot;ap-id 2099\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
if _, err := w.Write([]byte(&quot;ap-group xuebao-free\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
if _, err := w.Write([]byte(&quot;y\r&quot;)); err != nil {
panic(&quot;Failed to run: &quot; + err.Error())
}
in &lt;- &quot;quit&quot;
_ = &lt;-out
session.Wait()
}
func checkError(err error, info string) {
if err != nil {
fmt.Printf(&quot;%s. error: %s\n&quot;, info, err)
os.Exit(1)
}
}
func MuxShell(w io.Writer, r, e io.Reader) (chan&lt;- string, &lt;-chan string) {
in := make(chan string, 5)
out := make(chan string, 5)
var wg sync.WaitGroup
wg.Add(1) //for the shell itself
go func() {
for cmd := range in {
wg.Add(1)
w.Write([]byte(cmd + &quot;\n&quot;))
wg.Wait()
}
}()
go func() {
var (
buf [1024 * 1024]byte
t   int
)
for {
n, err := r.Read(buf[t:])
if err != nil {
fmt.Println(err.Error())
close(in)
close(out)
return
}
t += n
result := string(buf[:t])
if strings.Contains(string(buf[t-n:t]), &quot;More&quot;) {
w.Write([]byte(&quot;\n&quot;))
}
if strings.Contains(result, &quot;username:&quot;) ||
strings.Contains(result, &quot;password:&quot;) ||
strings.Contains(result, &quot;&gt;&quot;) {
out &lt;- string(buf[:t])
t = 0
wg.Done()
}
}
}()
return in, out
}

答案8

得分: -4

以下是翻译好的代码:

func main() {
    key, err := ioutil.ReadFile("你的密钥文件路径")
    if err != nil {
        panic(err)
    }
    signer, err := ssh.ParsePrivateKey([]byte(key))
    if err != nil {
        panic(err)
    }
    config := &ssh.ClientConfig{
        User: "ubuntu",
        Auth: []ssh.AuthMethod{
            ssh.PublicKeys(signer),
        },
    }
    client, err := ssh.Dial("tcp", "52.91.35.179:22", config)
    if err != nil {
        panic(err)
    }
    session, err := client.NewSession()
    if err != nil {
        panic(err)
    }
    defer session.Close()
    session.Stdout = os.Stdout
    session.Stderr = os.Stderr
    session.Stdin = os.Stdin
    session.Shell()
    session.Wait()
}

希望对你有帮助!

英文:

The following code works for me.

func main() {
key, err := ioutil.ReadFile(&quot;path to your key file&quot;)
if err != nil {
panic(err)
}
signer, err := ssh.ParsePrivateKey([]byte(key))
if err != nil {
panic(err)
}
config := &amp;ssh.ClientConfig{
User: &quot;ubuntu&quot;,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
}
client, err := ssh.Dial(&quot;tcp&quot;, &quot;52.91.35.179:22&quot;, config)
if err != nil {
panic(err)
}
session, err := client.NewSession()
if err != nil {
panic(err)
}
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
session.Shell()
session.Wait()
}

huangapple
  • 本文由 发表于 2014年6月27日 05:03:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/24440193.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定