os.Exec and /bin/sh: executing multiple commands - bash

I've run into an issue with the os/exec library. I want to run a shell and pass it multiple commands to run, but it's failing when I do. Here's my test code:
package main
import (
"fmt"
"os/exec"
)
func main() {
fmt.Printf("-- Test 1 --\n`")
command1 := fmt.Sprintf("\"%s\"", "pwd") // this one succeeds
fmt.Printf("Running: %s\n", command1)
cmd1 := exec.Command("/bin/sh", "-c", command1)
output1,err1 := cmd1.CombinedOutput()
if err1 != nil {
fmt.Printf("error: %v\n", err1)
return
}
fmt.Printf(string(output1))
fmt.Printf("-- Test 2 --\n")
command2 := fmt.Sprintf("\"%s\"", "pwd && pwd") // this one fails
fmt.Printf("Running: %s\n", command2)
cmd2 := exec.Command("/bin/sh", "-c", command2)
output2,err2 := cmd2.CombinedOutput()
if err2 != nil {
fmt.Printf("error: %v\n", err2)
return
}
fmt.Printf(string(output2))
}
When running this I get an error 127 on the second example. It seems like it's looking for a literal "pwd && pwd" command instead of evaluating it as a script.
If I do the same thing from the command line it works just fine.
$ /bin/sh -c "pwd && pwd"
I'm using Go 1.4 on OS X 10.10.2.

the quotes are for your shell where you typed the command line, they should not be included when programatically launching an app
just make this change and it will work:
command2 := "pwd && pwd" // you don't want the extra quotes

Related

Complex command in exec.Command()

I want to exec this command:
docker exec -it demoContainer bash -c "pip freeze" > "requirements.txt"
But don't find any example of this kind of complex commands with pipes, and output writes.
I tryed this:
cmd := exec.Command("docker", "exec -it demoContainer bash -c \"pip freeze\" > \"requirements.txt\"")
_, err = cmd.CombinedOutput()
And:
cmd := exec.Command("docker", "exec", "-it", "demoContainer", "bash", "-c", "pip freeze", ">", "scheduleDeploy/requirements.txt")
_, err = cmd.CombinedOutput()
But always crash with the following output:
Error: Exit status 1
Edit
Updates with answers ideas:
err = exec.Command("sh", "-c","'docker exec -it demoContainer bash -c 'pip freeze' > 'requirements.txt''").Run()
Output:
Exit error status 2
You don't have to use the shell for redirection, you can just let Go do it:
package main
import (
"os"
"os/exec"
)
func main() {
f, e := os.Create("requirements.txt")
if e != nil {
panic(e)
}
defer f.Close()
c := exec.Command(
"docker", "exec", "-it", "demoContainer", "bash", "-c", "pip freeze",
)
c.Stdout = f
c.Run()
}
The > redirection is a "shell" thing, not an "exec" thing. You will need to do one of "run bash, with the command you want executed" or "open a file, set the resulting os.File as the Stdout of the exec.Command, before starting it".
Either would probably solve the issue you are facing.
To use the "bash -c" version, something like:
exec.Command("bash", "-c", `docker exec -it demoContainer bash -c "pip freeze" > requirements.txt`)

Using exec.Command in golang, how do I open a new terminal and execute a command?

I am able to get the following command to open a new terminal and execute the command when I directly input it inside a terminal, but I can not get it to work when I use the exec.Commmand function in go.
osascript -e 'tell application "Terminal" to do script "echo hello"'
I think the issue lies within the double and single quotes, but I am not exactly sure what is causing the error.
c := exec.Command("osascript", "-e", "'tell", "application", `"Terminal"`, "to", "do", "script", `"echo`, `hello"'`)
if err := c.Run(); err != nil {
fmt.Println("Error: ", err)
}
As of now the code is returning Error: exit status 1, but I would like the code to open a terminal window and execute the command.
After playing some time found this:
cmd := exec.Command("osascript", "-s", "h", "-e",`tell application "Terminal" to do script "echo test"`)
Apparently you should give the automator script in 1 argument and also use ticks (`) as the string literals for go.
"-s", "h" is for automator program to tell you human readable errors.
My complete test code is as follows:
package main
import (
"fmt"
"os/exec"
"io/ioutil"
"log"
"os"
)
// this is a comment
func main() {
// osascript -e 'tell application "Terminal" to do script "echo hello"'
cmd := exec.Command(`osascript`, "-s", "h", "-e",`tell application "Terminal" to do script "echo test"`)
// cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
stderr, err := cmd.StderrPipe()
log.SetOutput(os.Stderr)
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
slurp, _ := ioutil.ReadAll(stderr)
fmt.Printf("%s\n", slurp)
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
}
PS: osascript is the command line interface for Automator app in macOS.

is there any way run multiple command "os/exec" in one proccess in golang?

i want run multiple command in "os/exec" in one process, as you see below some command like "cd" not worked.
func main() {
cmd := exec.Command("ls")
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
err := cmd.Run()
fmt.Print(string(cmdOutput.Bytes()))
fmt.Println(".......... cd .........")
cdOutput := &bytes.Buffer{}
cdcomand:=exec.Command("cd","model")
cdcomand.Stdout = cdOutput
err = cdcomand.Run()
fmt.Print(string(cdOutput.Bytes()))
fmt.Println(".......... ls .........")
lsOutput := &bytes.Buffer{}
lscmd:=exec.Command("ls")
lscmd.Stdout = lsOutput
err = lscmd.Run()
if err != nil {
os.Stderr.WriteString(err.Error())
}
fmt.Print(string(lsOutput.Bytes()))}
i try with another way:
package main
import (
"os/exec"
"bytes"
"os"
"fmt"
)
func main() {
cmd := exec.Command("ls")
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
err := cmd.Run()
fmt.Print(string(cmdOutput.Bytes()))
fmt.Println(".......... cd and ls .........")
cdOutput := &bytes.Buffer{}
cdcomand:= exec.Command("sh", "-c", "ls && cd model")
cdcomand.Stdout = cdOutput
err = cdcomand.Run()
fmt.Print(string(cdOutput.Bytes()))
fmt.Println(".......... ls .........")
lsOutput := &bytes.Buffer{}
lscmd:=exec.Command("ls")
lscmd.Stdout = lsOutput
err = lscmd.Run()
if err != nil {
os.Stderr.WriteString(err.Error())
}
fmt.Print(string(lsOutput.Bytes()))
}
it did not work too.
in cmd document writes:
A Cmd cannot be reused after calling its Run, Output or CombinedOutput methods.
i've searched all tuts and docs for a way to do this, but i could not find any things. there was no solution in Executing external commands in Go article and advanced command execution in Go with os
each cmd command execute in different process so command like "cd" will not change directory . is there any way run multiple command "os/exec" in one proccess in golang?
Yes!
You could use sh -c "ls && cd model"
cmd := exec.Command("sh", "-c", "ls && cd model")
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err = cmd.Run()
On ubuntu
$ man sh
DASH(1) BSD General Commands Manual DASH(1)
NAME
dash — command interpreter (shell)
-c Read commands from the command_string operand instead of from the standard input. Special parameter 0 will be set from the command_name
operand and the positional parameters ($1, $2, etc.) set from the remaining argument operands.
An example using:
$ go version
go version go1.10.2 linux/amd64
// cmd/test/main.go
package main
import (
"bytes"
"os/exec"
"fmt"
)
func main() {
var stdout, stderr bytes.Buffer
cmd := exec.Command("sh", "-c", "echo 'hello' && echo 'hi'")
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
fmt.Println(err)
out := stdout.String() + stderr.String()
fmt.Printf(out)
}
$ go run cmd/test/main.go
<nil>
hello
hi

Call source from inside a Go program

For fun and to better learn Go, I'm trying to re-implement antigen in Go.
Problem is: source is a shell built-in function, so I can't call it with os/exec Command function, because it expects an executable in PATH.
How can I do this? And, is it possible to make a source from inside a go program affect the user shell?
You can write the command directly in the terminal device. But, to do that, first you need to know which device is using the user. A script that executes your program can be a solution.
#!/bin/bash
echo Running from foo script, pid = $$
go run foo.go `tty`
Then, the program has to write the commands to the terminal device.
package main
import (
"C"
"fmt"
"os"
"syscall"
"unsafe"
)
func main() {
// Get tty path
if len(os.Args) < 2 {
fmt.Printf("no tty path\n")
os.Exit(1)
}
ttyPath := os.Args[1]
// Open tty
tty, err := os.Open(ttyPath)
if err != nil {
fmt.Printf("error opening tty: %s\n", err.Error())
os.Exit(2)
}
defer tty.Close()
// Write a command
cmd := "echo Hello from go, pid = $$\n"
cmdstr := C.CString(cmd)
cmdaddr := uintptr(unsafe.Pointer(cmdstr))
for i := range []byte(cmd) {
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, tty.Fd(), syscall.TIOCSTI, cmdaddr+uintptr(i))
if uintptr(err) != 0 {
fmt.Printf("syscall error: %s\n", err.Error())
os.Exit(3)
}
}
}
Here is an example output:
$ echo $$
70318
$ ./foo
Running from foo script, pid = 83035
echo Hello from go, pid = $$
$ echo Hello from go, pid = $$
Hello from go, pid = 70318
Note that I am executing the script with ./ not source, so the PID of the script differs. But later, the command executed by the go program has the same PID.

Executing docker command using golang exec fails

I am using cmd.go (see below) to execute a docker command but it fails. I do the following steps to execute and get the following error.
go build
sudo ./cmd
Output:
docker run -v ~/exp/a.out:/a.out ubuntu:14.04 /a.out -m 10m
2014/10/16 14:32:12 exit status 1
On the other hand running directly as
sudo docker run -v ~/exp/a.out:/a.out ubuntu:14.04 /a.out -m 10m
results in the correct output of a.out.
Hello World
This is the code of cmd.go. How can I get it to work? Thanks!
package main
import (
"fmt"
"log"
"os/exec"
"strings"
)
func ExampleCmd_Output() {
//out, err := exec.Command("date", "--version").Output() // This works
//out, err := exec.Command("docker", "--version").Output() // This works
//out, err := exec.Command(cmd, "images").Output() // Even docker images command works!
cmd := "docker"
cmdArgs := []string{"run", "-v", "~/exp/a.out:/a.out", "ubuntu:14.04", "/a.out", "-m", "10m"}
fmt.Println(cmd + " " + strings.Join(cmdArgs, " "))
out, err := exec.Command(cmd, cmdArgs...).Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", out)
}
func main() {
ExampleCmd_Output()
}
EDIT: After a comment, I tried executing the command "docker images". It works if I run the executable with sudo. That is, I am using the following line in the code now.
out, err := exec.Command(cmd, "images").Output()
After doing go build and running "sudo ./cmd", I get the output of docker images command. However, without sudo, I still get exit status 1. But with docker run command above even with sudo, I don't get an output.
Thanks to Os Exec Sudo Command in Go, I am now able to do what I want.
func main() {
cmdStr := "sudo docker run -v ~/exp/a.out:/a.out ubuntu:14.04 /a.out -m 10m"
out, _ := exec.Command("/bin/sh", "-c", cmdStr).Output()
fmt.Printf("%s", out)
}
Output:
Hello World

Resources