How to capture/log everything after spawning an interactive program - go

I have a method that can spawn an interactive process, now how do I log everything (including stdin and stdout) after spawning ?
e.g.,
func execute(cmd1 string, slice []string) {
cmd := exec.Command(cmd1, slice...)
// redirect the output to terminal
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Run()
}
..
The interactive program could be :
execute(ftp)
I think I have to dup stdin, stdout and read write in separate thread.

Rather than redirecting it's output to the terminal read it and then you can log/print do whatever you want with it.
stdout, err := cmd.StdoutPipe()
b, _ := ioutil.ReadAll(stdout)
fmt.Println(string(b))
Something like the code above would work though there are many options. I think you'll want to remove all that code you have to redirect to the terminal.

you could store the output in a temporary buffer and write it to several places
outBuf := bytes.Buffer{}
cmd := exec.Command(cmd1, slice...)
cmd.Stdout = &outBuf
cmd.Run()
if outBuf.Len() > 0 {
log.Printf("%s", outBuf.String())
fmt.Fprintf(os.Stdout, "%s", outBuf.String())
}

Related

Go: How to prevent pipes from blocking when executing a command via os.exec?

In Go version 1.14.2, the following is some sample code which illustrates the often-suggested way to spawn a command, feed data to it via stdin, and then capture its stdout and stderr. It makes use of pipes, but this can cause the program to block if either the stdout or stderr pipe's buffers become full before the process finishes (error handling is omitted in this example) ...
func ExecExample(cmd *exec.Cmd, input *[]byte) {
stdout, err := cmd.StdoutPipe()
stderr, err := cmd.StderrPipe()
stdin, err := cmd.StdinPipe()
err = cmd.Start()
stdin.Write(*input)
stdin.Close()
stdoutBytes, err := ioutil.ReadAll(stdout)
stderrBytes, err := ioutil.ReadAll(stderr)
waitErr := cmd.Wait()
}
How could this sample code be restructured in Go so that the stdout and stderr pipes never block?
Also, note that I'm looking for a way to separately capture the stdout and stderr output. I do not want them to be combined.
Thank you in advance.
Let the exec package do the work for you.
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd.Stdin = bytes.NewReader(*input)
err := cmd.Run()
stdoutBytes := stdout.Bytes()
stderrBytes := stderr.Bytes()
The exec package creates and manages the goroutines needed to pump data to and from the child process.
Run it in the playground.
Read one (or both) streams in a goroutine:
var stderrBytes []byte
go func() {
stderrBytes, _ = ioutil.ReadAll(stderr)
}()
err = cmd.Start()
stdoutBytes, err := ioutil.ReadAll(stdout)

How to print output of an interactive child process from parent process?

I am trying to get output of a interactive child process like python from the parent process. I have tried the following code to change the processes stdin to os.Stdin and stdout to os.Stdout but it isn't working. I can't see the output from the child process on the parent's terminal. Am I missing something or doing it wrong?
func main(){
cmd := exec.Command("python")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil{
fmt.Println("Cannot Execute cmd.")
}
}
Run exec.Command("python", "-i").
By default when running python not in a shell it won't enter interactive mode and won't print anything out.

How to print to stdout while save the stdout to string at the same time in golang?

What I want to do is like:
cmd := exec.Command(someCommand)
cmd.Stdout = os.Stdout
cmd.Run()
save(os.Stdout)
Because this command takes a long time executing, I want to print results on the screen immediately. So I do not want to use result := cmd.Output() fmt.Print(result) to save the output and then print
Usa a MultiWriter:
cmd := exec.Command(someCommand)
var buf bytes.Buffer
cmd.Stdout = io.MultiWriter(os.Stdout, &buf)
cmd.Run()
save(buf.Bytes()) // Bytes() returns a []byte containing the stdout from the commmand.

Go - write to stdin on external command

I have the following code which executes an external command and output to the console two fields waiting for the user input.
One for the username and other for the password, and then I have added them manually.
Could anyone give me a hint about how to write to stdin in order to enter these inputs from inside the program?
The tricky part for me is that there are two different fields waiting for input, and I'm having trouble to figure out how to fill one after the other.
login := exec.Command(cmd, "login")
login.Stdout = os.Stdout
login.Stdin = os.Stdin
login.Stderr = os.Stderr
err := login.Run()
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
SOLUTION:
login := exec.Command(cmd, "login")
var b bytes.Buffer
b.Write([]byte(username + "\n" + pwd + "\n"))
login.Stdout = os.Stdout
login.Stdin = &b
login.Stderr = os.Stderr
I imagine you could use a bytes.Buffer for that.
Something like that:
login := exec.Command(cmd, "login")
buffer := bytes.Buffer{}
buffer.Write([]byte("username\npassword\n"))
login.Stdin = &buffer
login.Stdout = os.Stdout
login.Stderr = os.Stderr
err := login.Run()
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
The trick is that the stdin is merely a char buffer, and when reading the credentials, it will simply read chars until encountering a \n character (or maybe \n\r). So you can write them in a buffer in advance, and feed the buffer directly to the command.

Executing web2exe from Golang is giving me 'exit status 2'

Im trying the following, to use go to bundle a folder of html files using the CMD web2exe.
cmd := exec.Command("web2exe-win.exe", "html-folder --main index.html --export- to windows-x32 --output-dir")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
fmt.Println(out)
When a program exits non-zero it means that it could not run successfully and typically it has written an error message to STDERR (or STDOUT). You should somehow capture or print the output streams so you can inspect them for error messages. For example:
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
Note also that your command line arguments should be separate array elements (instead of space separated elements in a single string as they are now):
cmd := exec.Command("web2exe-win.exe", "html-folder", "--main", "index.html", "--export-to", "windows-x32", "--output-dir")

Resources