Spawning a docker command yields an empty result - go

I have the following Go code, where I try to spawn docker command and get its output:
package main
import "fmt"
import "os/exec"
func main() {
cmd := exec.Command("docker")
cmdOutput, err := cmd.Output()
if (err != nil) {
panic(err)
}
fmt.Println(string(cmdOutput))
}
But when I run this code as a result I get an empty string, which is stange since I get an output when I run docker command directly in a command line.
Moreover, this same code yields results just fine if I spawn other commands with it, for example ls.
What may be wrong here?

try using cmd.CombinedOutput()

Related

How to properly "go build" using exec.Command with many arguments?

I'm trying to compile a go package using exec.Command. I was successful with the arguments being "go" and "build" as such below:
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
out, err := exec.Command("go", "build").Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(out)
}
However, while trying to perform a "go build" with more arguments it seems that I'm doing something wrong? This is what my code looks like:
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
out, err := exec.Command("set", "GOOS=js&&", "set", "GOARCH=wasm&&", "go", "build", "-o", "C:/Users/Daniel/Desktop/go-workspace/src/sandbox/other/wasm/assets/json.wasm", "kard").Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(out)
}
The output is exec: "set": executable file not found in %PATH%
The normal command I would perform for this in the command line would be set GOOS=js&& set GOARCH=wasm&& go build -o C:\Users\Daniel\Desktop\go-workspace\src\sandbox\other\wasm\assets\json.wasm kard.
I assume there is something I'm misunderstanding with using exec.Command? I really appreciate any support.
The application uses shell syntax to set the environment variables, but the exec package does not use a shell (unless the command that you are running is a shell).
Use the command environment to specify the environment variables for the exec'ed command.
The go build does not normally write to stdout, but it does write errors to stderr. Use CombinedOutput instead of Output to easily capture error text.
cmd := exec.Command("go", "build", "-o", "C:/Users/Daniel/Desktop/go-workspace/src/sandbox/other/wasm/assets/json.wasm", "kard")
cmd.Env = []string{"GOOS=js", "GOARCH=wasm"}
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("%v: %s\n", err, out)
}

Go : Correctly running external program with arguments

Good evening,
I'm working on converting some tools written in python to Go in order to better understand it.
I need the program to call an external .exe with some arguments in order for it to correctly format some data. In the windows shell I can do C:\path_to_exe\file.exe arg1 arg2 "C:\path_to_output\output.txt"
I believe the correct method to do this in Go would be using exec.Command, but I'm not getting any...meaningful results.
out, err := exec.Command("cmd", "C:\\path\\tools\\util\\Utility.exe C:\\file_Location \"select * from TABLE\" C:\\output_path\\output.txt").Output()
fmt.Printf("\n", string(out))
if err != nil {
println(" Error running decomp ", err)
}
This appears to be running command, as the output I am receiving is:
%!(EXTRA string=Microsoft Windows [Version 10.0.22000.739]
(c) Microsoft Corporation. All rights reserved.
Process finished with the exit code 0
Just for giggles I tried breaking up the arguments, but the same result was achieved
out, err := exec.Command("cmd", exPath, utilPath, statement, textOutputPath+"test.txt").Output()
I'm expecting the executed program to run, parse the correct file based on the input, and output the specified txt file. I am left with no .txt file, and the go program runs much faster then the parsing should take.
There must be something I'm missing, could someone please provide some insight on the correct usage of exec.Command, because every example I can find appears to show that this should work.
Why are you spawning cmd.exe and having it run your utility.exe?
You can just spawn utility on its own.
For instance, suppose you have two binaries, hello and say-hello living in the same directory, compiled from
hello.go → hello:
package main
import (
"fmt"
"os"
)
func main() {
argv := os.Args[1:]
if len(argv) == 0 {
argv = []string{"world"}
}
for _, arg := range argv {
fmt.Printf("Hello, %s!\n", arg)
}
}
say-hello.go → say-hello:
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
process := exec.Command("./hello", os.Args[1:]...)
process.Stdin = os.Stdin
process.Stdout = os.Stdout
process.Stderr = os.Stderr
if err := process.Run(); err != nil {
fmt.Printf("Command failed with exit code %d\n", process.ProcessState.ExitCode())
fmt.Println(err)
}
}
You can then run the command:
$ ./say-hello Arawn Gywdion Sarah Hannah
And get back the expected
Hello, Arawn!
Hello, Gwydion!
Hello, Sarah!
Hello, Hannah!
It appears to be working correctly according to the outputs in your question.
A few suggestions:
It might be useful to print the command out as a string before running it, to check it's what you want.
You may find backticks useful when you have a string containing backslashes and quotation marks.
You have not supplied any format to fmt.Printf, hence the EXTRA in that output.
Using println to print the error will not stringify it, so use fmt.Printf for that too.
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("cmd", `C:\path\tools\util\Utility.exe C:\file_Location "select * from TABLE" C:\output_path\output.txt`)
fmt.Printf("%s\n", cmd.String())
out, err := cmd.Output()
fmt.Printf("%s\n", string(out))
if err != nil {
fmt.Printf(" Error running decomp %s\n", err)
}
}
Playground: https://go.dev/play/p/3t0aOxAZRtU

Called program fails if run through os.exec

I am not sure if this is a problem of the called program or if the problem is caused by the way I call the program. Because of this I start at the source code.
I need to call ssh from a program (if you are interested in the reasons I will mention them below) but ssh silently exits.
When I call ssh -v user#remotehost from shell this succeeds:
the wanted debug output on stderr is shown
I am asked for the password
I can see the remote hosts shell
But when I do the same from within my program (myssh -v user#remotehost only this happens:
I am asked for the password
Neither the debug output on stderr is shown nor do I reach the remote hosts shell.
This is my sourcecode:
package main
import (
"fmt"
"log"
"os"
"os/exec"
)
func main() {
params := os.Args[1:]
fmt.Printf("passing this to ssh: %s\n", params)
cmd := exec.Command("ssh", params...)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
Reason why I wrote this code: I use Ansible which calls ssh. I need to "manipulate" the parameters that Ansible passes to ssh. So far I asked on the OpenSSH and Ansible mailing lists, there is no means in OpenSSH and Ansible to change the parameters (for others it is, but not those I need). The best suggestion I got and that I want to implement is to provide an alternative ssh command to Ansible, use that to receive and modify the parameters and pass them on to the real ssh.
Are you capturing Stdout and Stderr from cmd? This is where the respective outputs of the command is sent to. The documentation for exec.Command has a really good example on this.
In your case, you'll also want to setup Stdin so that you can pass the password for example.
Here's a really basic example based on your code:
package main
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
)
func main() {
params := os.Args[1:]
fmt.Println("Passing this to ssh: %s", params)
var stdin bytes.Buffer
var stdout bytes.Buffer
var stderr bytes.Bufer
cmd := exec.Command("ssh", params...)
cmd.Stdin = &stdin
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
fmt.Println("Stdout: %s", stdout.String())
fmt.Println("stderr: %s", stderr.String())
}
Since stdin, stdout, and stderr are all bytes.Buffers, you can read and write from them just like any other buffer.
You might also want to consider using the golang.org/x/crypto/ssh package which provides a native SSH interface for Go instead of using sub-processes.

Using golang connect to docker container with functional tty

Start simple Docker container in Detached(Background) mode
docker run -d --name test ubuntu tail -f /dev/null
Here comes my simple golang code where I connect to running container.
In current connection I want to get functional tty.
package main
import (
"fmt"
"os/exec"
"bufio"
"io"
"os"
"github.com/kr/pty"
)
func main() {
cmd := exec.Command("docker", "exec", "-it", "test", "bin/bash")
tty, err := pty.Start(cmd)
if err != nil {
fmt.Println("Error start cmd", err)
}
defer tty.Close()
go func() {
scanner := bufio.NewScanner(tty)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}()
go func() {
io.Copy(tty, os.Stdin)
}()
err = cmd.Wait()
if err != nil {
fmt.Println("Error Waiting", err)
}
}
More less it works, but there is couple thinks which is not working as I would run docker command from my terminal.
After login I don't see prompt, like root#ba351b44ca80:/# only after hitting return it appears, but my currsor is in new line where are no prompt;
Also arrow up to get previous command is not working
Only prints out
root#ba351b44ca80:/#
^[[A^[[A^[[A
but behind scene previous command is selected and by hitting return it is executed.
After executing command for cursor is not displayed prompt, like
root#ba351b44ca80:/# ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
<Here my cursor>
go-dockerclient is worth checking out. It is a simple nice abstraction of the Docker remote API. It is used by many opensource projects and is regularly maintained as well.

Go: execute bash script

How do I execute a bash script from my Go program? Here's my code:
Dir Structure:
/hello/
public/
js/
hello.js
templates
hello.html
hello.go
hello.sh
hello.go
cmd, err := exec.Command("/bin/sh", "hello.sh")
if err != nil {
fmt.Println(err)
}
When I run hello.go and call the relevant route, I get this on my console:
exit status 127
output is
I'm expecting ["a", "b", "c"]
I am aware there is a similar question on SO: Executing a Bash Script from Golang, however, I'm not sure if I'm getting the path correct. Will appreciate help!
exec.Command() returns a struct that can be used for other commands like Run
If you're only looking for the output of the command try this:
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
out, err := exec.Command("date").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
You can also use CombinedOutput() instead of Output(). It will dump standard error result of executed command instead of just returning error code.
See:
How to debug "exit status 1" error when running exec.Command in Golang
Check the example at http://golang.org/pkg/os/exec/#Command
You can try by using an output buffer and assigning it to the Stdout of the cmd you create, as follows:
var out bytes.Buffer
cmd.Stdout = &out
You can then run the command using
cmd.Run()
If this executes fine (meaning it returns nil), the output of the command will be in the out buffer, the string version of which can be obtained with
out.String()

Resources