source code is:
//this code run in parent process
cmd:=exec.Command("cmd","/c","./child.exe")
if e:=cmd.Start();e!=nil{
//todo handle error
}
//child.exe handle task maybe long time
os.Exit() //notice!!! parent process will kill itself because test.exe will rm parent process
My problem is test.exe have anything log info in cmd.
More or less like this:
cmd := exec.Command("cmd", "/c", "./child.exe")
out := bytes.NewBuffer(nil)
cmd.Stdout = out
if err := cmd.Run(); err != nil {
}
fmt.Println(out.String())
Related
I am trying to write a file from a bash command into a file in Go.
Note there are several reasons for using Go over bash here: I have some more logic such as parsing configuration files, I would like to run that code for multiple DBs in parallele and finally performing some more complex data manipulation after.
dumpStr := fmt.Sprintf("pg_dump -U %s -h %s %s | gzip", DbUserName, DbHost, DbName)
cmd := exec.Command("bash", "-c", dumpStr)
cmd.Env = append(cmd.Env, "PGPASSWORD="+DbPassword)
outfile, err := os.Create(DbName + ".gz")
if err != nil {
panic(err)
}
outfile = cmd.Stdout
defer outfile.Close()
err = cmd.Start()
if err != nil {
panic(err)
}
cmd.Wait()
However, I am getting an emtpy result.
I am getting data if I am executing dumpStr from the CLI but not from that code...
What am I missing?
As Flimzy said, you're not capturing the output of pg_dump. You can do that with Go, or you can use pg_dump-s --file. It can also compress with --compress so no need to pipe to gzip. Then there's no need for bash and you can avoid shell quoting issues.
cmd := exec.Command(
"pg_dump",
"--compress=9",
"--file="+DbName + ".gz",
"-U"+DbUserName,
"-h"+DbHost,
DbName,
)
log.Print("Running pg_dump...")
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
Much simpler and more secure.
For illustration here's how you'd do it all in Go.
Use Cmd.StdoutPipe to get an open IO reader to pg_dump's stdout. Then use io.Copy to copy from stdout to your open file.
#Peter points out that since Cmd.Stdout is an io.Reader it's simpler to assign the open file to cmd.Stdout and let cmd write to it directly.
// Same as above, but no --file.
cmd := exec.Command(
"pg_dump",
"--compress=9",
"-U"+DbUserName,
"-h"+DbHost,
DbName,
)
// Open the output file
outfile, err := os.Create(DbName + ".gz")
if err != nil {
log.Fatal(err)
}
defer outfile.Close()
// Send stdout to the outfile. cmd.Stdout will take any io.Writer.
cmd.Stdout = outfile
// Start the command
if err = cmd.Start(); err != nil {
log.Fatal(err)
}
log.Print("Waiting for command to finish...")
// Wait for the command to finish.
if err = cmd.Wait(); err != nil {
log.Fatal(err)
}
In addition, you're only checking if the command started, not if it successfully ran.
From the docs for Cmd.Start.
Start starts the specified command but does not wait for it to complete.
The Wait method will return the exit code and release associated resources once the command exits.
You're checking cmd.Start for an error, but not cmd.Wait. Checking the error from cmd.Start only means the command started. If there is an error while the program is running you won't know what it is.
You need to actually use the output of your command. You're not doing that. To do so, use the StdoutPipe method, then you can copy the stdout from your program, into your file.
I want to switch to the bash shell from my custom shell in go.
I am not sure how to sleep my parent process (custom shell) and switch to the child process (bash)
This is my part of the code.
cmd := exec.Command("bash", "-c", "/bin/bash")
stdoutStderr, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf(err.Error())
}
fmt.Printf("%s\n", stdoutStderr)
I want to do it as follows:
myshell >> /bin/bash
$ /bin/myshell
myshell >>
the code is exec but not fork
binary, lookErr := exec.LookPath("/bin/bash")
if lookErr != nil {
panic(lookErr)
}
args := []string{"/bin/bash"}
env := os.Environ()
execErr := syscall.Exec(binary, args, env)
if execErr != nil {
panic(execErr)
}
so if I exit the bash shell, of course, my custom shell is killed.
myshell> bash
bash-3.2$ exit
exit
This is a recipe for how to stop a process and let the parent shell process take over control. I'm not sure if you can use it to do the opposite.
The idea is to send a stop signal to yourself.
pid := os.Getpid()
process, err := os.FindProcess(pid)
if err != nil {
err = errors.Wrapf(err, "error pausing process %+v", process)
return
}
process.Signal(syscall.SIGSTOP)
Source https://github.com/grzegorz-zur/bare-minimum/blob/95fb116631b6146707c47455d8ffce6bb5b8717c/editor.go#L152
Earlier you need to listen in a goroutine to continue signal to reinitialize your process if needed
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGCONT, syscall.SIGTERM)
for signal := range signals {
switch signal {
case syscall.SIGCONT:
// reinitialize
case syscall.SIGTERM:
// stop
}
}
Source https://github.com/grzegorz-zur/bare-minimum/blob/95fb116631b6146707c47455d8ffce6bb5b8717c/editor.go#L87
I have resolved this problem by this way.
package main
import (
"os/exec"
)
func main() {
cmd := exec.Command("sh", "-c", "bash")
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
cmd.Run()
}
I have a Golang code which must run a detached child process.
The Linux version of my implementation using syscall.ForkExec like this.
syscall.ForkExec(my_program, []string{}, nil)
But I can't found Windows implementation. I have found a proposition using START /B.
cmd := exec.Command("START", "/B", my_program)
cmd.Start()
Unfortunately, START can't be found and I have no other solution using Golang.
start is not a standalone application, it's an (internal) command of the Windows command line interpreter (cmd.exe) (details: Command line reference / Start), so you need a "shell" to run the start command.
Use cmd.exe with the /C parameter, and pass start and your application to run.
Like in this example:
s := []string{"cmd.exe", "/C", "start", `c:\path\to\your\app\myapp.exe`}
cmd := exec.Command(s[0], s[1:]...)
if err := cmd.Run(); err != nil {
log.Println("Error:", err)
}
Or without the command slice:
cmd := exec.Command("cmd.exe", "/C", "start", `c:\path\to\your\app\myapp.exe`)
if err := cmd.Run(); err != nil {
log.Println("Error:", err)
}
You may also pass the /b param to start like this if you don't want a terminal window for the launched application:
cmd := exec.Command("cmd.exe", "/C", "start", "/b", `c:\path\to\your\app\myapp.exe`)
if err := cmd.Run(); err != nil {
log.Println("Error:", err)
}
It's not clear to me what you mean by detached. If you mean in the sense, "don't wait for program to finish", you don't need a shell for that:
package main
import "os/exec"
func main() {
exec.Command("firefox", "google.com/search?q=golang").Start()
}
https://golang.org/pkg/os/exec#Cmd.Start
I am trying to make a go program that calls another program (with a GUI) by command line.
Using os/exec package. The external program starts, but it always runs in the background. (Even if I try to start "Notepad.exe" or a .bat file which then calls the actual program.)
In the task manager you can see the process but there no way to interact with it.
Here's an example of code I've been experimenting with:
cmd := exec.Command("cmd.exe", "/C", "start", "\"\"", `Notepad.exe`)
log.Println("cmd.exe", "/C", "start", "\"\"", `Notepad.exe`)
cmd.Stdout = os.Stdout
if err := cmd.Start(); err != nil {
log.Println("Error: ", err)
}
Is there a way to alter the code so it starts "Notepad.exe" in a way it can be interacted with via the UI?
I am trying this with a program on Windows compiled on Mac.
You don't need that empty "" parameter, simply use:
cmd := exec.Command("cmd.exe", "/C", "start", "notepad.exe")
Also notepad.exe won't write anything to its standard output, so you don't need to set it. Simply run it like:
s := []string{"cmd.exe", "/C", "start", "notepad.exe"}
log.Println("Starting", s)
cmd := exec.Command(s[0], s[1:]...)
if err := cmd.Run(); err != nil {
log.Println("Error:", err)
}
I'm trying to start a command in a detached process so that it can continue after go program exits. I need to redirect the output of the command to a file.
What I need is something like this:
func main() {
command := exec.Command("/tmp/test.sh", ">", "/tmp/out")
if err := command.Start(); err != nil {
fmt.Fprintln(os.Stderr, "Command failed.", err)
os.Exit(1)
}
fmt.Println("Process ID:", command.Process.Pid)
}
Obviously such redirect doesn't work. As I immediately exit from the program after starting the long running command, I cannot open a file and bind it to the Stdout.
Is there any way to achieve such a redirect?
You may start a shell which executes your command / app, and you may redirect its output to a file. The shell will continue to run and execute your script / app even if your Go app exits.
Example:
cmd := exec.Command("sh", "-c", "/tmp/test.sh > /tmp/out")
if err := cmd.Start(); err != nil {
panic(err)
}
fmt.Println("Process ID:", cmd.Process.Pid)
Test it with this simple Go app (replace /tmp/test.sh with the name of the executable binary you compile this into):
package main
import ("fmt"; "time")
func main() {
for i := 0; i < 10; i++ {
fmt.Printf("%d.: %v\n", i, time.Now())
time.Sleep(time.Second)
}
}
This app simply prints a line to the standard output once every second. You can see how the output file is being written e.g. with tail -f /tmp/out.
Note that you may use other shells to execute your scripts to your liking (and to what the test.sh script dictates).
For example to use bash:
cmd := exec.Command("/bin/bash", "-c", "/tmp/test.sh > /tmp/out")
// rest is unchanged
Note that the command to be executed by the shell is passed as a single string argument, and it is not broken down into multiple as you would do it if you were to execute it directly in the command prompt.
Maybe you can try to use this: https://stackoverflow.com/a/28918814/2728768
Opening a file (and os.File implements io.Writer), and then passing it as the command.Stdout could do the trick:
func main() {
command := exec.Command("./tmp/test.sh")
f, err := os.OpenFile("/tmp/out", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("error opening file: %v", err)
}
defer f.Close()
// On this line you're going to redirect the output to a file
command.Stdout = f
if err := command.Start(); err != nil {
fmt.Fprintln(os.Stderr, "Command failed.", err)
os.Exit(1)
}
fmt.Println("Process ID:", command.Process.Pid)
}
Not sure this could be a viable solution for your case. I've tried it locally and it seems working... remember that your user should be able to create/update the file.