I need to run multiple commands in the same SSH Session. Between each command I'm sending. I need to parse the output.
I found I can printout the first line from STDOUT, but when I try and print everything it just hangs, seeming waiting for STDOUT. This works for printing the first line, I can't figure out how to print all from STDOUT and release back to the program to run the next command.
I need to run a command, parse the output, then run a second command within the same session.
Example of my code.
package main
import (
"bufio"
"fmt"
"log"
"golang.org/x/crypto/ssh"
)
func main() {
// SSH credentials
sshConfig := &ssh.ClientConfig{
User: "rob",
Auth: []ssh.AuthMethod{
ssh.Password("OMGAPASSWORD....EEEEEEE"),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// Connect to SSH server
client, err := ssh.Dial("tcp", "*************:22", sshConfig)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Open a new session
session, err := client.NewSession()
if err != nil {
log.Fatal(err)
}
defer session.Close()
// Set up input and output streams
sshIn, _ := session.StdinPipe()
sshOut, _ := session.StdoutPipe()
//sshErr, _ := session.StderrPipe()
// Start the SSH session
if err := session.Start("/bin/bash"); err != nil {
log.Fatalf("Failed to start SSH session: %v", err)
}
// Send the first command
cmd1 := "ls -al"
fmt.Fprintf(sshIn, "%s\n", cmd1)
lines := make(chan string)
go func() {
scanner := bufio.NewScanner(sshOut)
for scanner.Scan() {
lines <- scanner.Text()
}
close(lines)
}()
for line := range lines {
fmt.Println(line)
}
}
session.Start("/bin/bash")
Trying to run sequential commands through a persistent shell is the root of all your difficulties. Since bash is what you're reading to and writing from, you can't easily tell when each command is successful.
Instead, start a new session with each command. Copy stdout and stderr to buffers, then you can do whatever you want with them.
for _, cmd := range []string{"ls -al", `bc <<<"3/2.0"`} {
session, err := client.NewSession()
if err != nil {
log.Fatal(err)
}
var stdout, stderr bytes.Buffer
// Set up input and output streams
//sshIn, _ := session.StdinPipe()
sshOut, _ := session.StdoutPipe()
sshErr, _ := session.StderrPipe()
go io.Copy(&stdout, sshOut)
go io.Copy(&stderr, sshErr)
// Start the command
if err := session.Start(cmd); err != nil {
log.Fatalf("Failed to start SSH session: %v", err)
}
if err := session.Wait(); err != nil {
log.Fatal(fmt.Errorf("error waiting: %w", err))
}
session.Close()
fmt.Printf("Command: \t%s\nStdout: \t%s\nStderr:%s\n", cmd, stdout.String(), stderr.String())
}
This way you know when each sequential command ends and whose output is which.
Full example should work locally if you have an ssh key pair in ~/.ssh, but doesn't work in playground because there's no SSh server to connect to (or key pair). Start a local ssh server like this:
docker run --rm -d -e PUBLIC_KEY="$(cat ~/.ssh/id_rsa.pub)" -e USER_NAME=$(whoami) -p 2222:2222 linuxserver/openssh-server
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With