I use the following code which is execute command of "npm install" , now while debug I see that the command is taking about 10..15 sec to execute (depend on how much module I've). what I want is that this command will execute in background and the program will continue.
cmd := exec.Command(name ,args...)
cmd.Dir = entryPath
In debug I see that to move to next line tass about 10..15 sec...
I’ve two questions in this:
npm install
is done I need to
do other stuff.start /B command is the most given answer, but the command will be closed when the terminal closed. and then CTRL C, close the terminal, the command will continue to run in background. This is the most decent way I have found so far.
In the new screen session, execute the command or script you wish to put in the background. Press Ctrl + A on your keyboard, and then D . This will detach the screen, then you can close the terminal, logout of your SSH session, etc, and the screen will persist.
If you want to push a command into the background, using & at the end is an easy way to do that. This way, you can issue a command in the background and continue to use your terminal as it runs. It comes with a catch, though. Using & doesn't disconnect the command away from you; it just pushes it into the background.
While in general you need goroutines to run something parallel (or more precisely concurrent), in case of running an external command or app in that manner does not require you to use goroutines (in fact, it's redundant).
This is because the exec.Cmd
used to run commands has a Cmd.Start()
method which starts the specified command but does not wait for it to complete. So you are free to do other stuff while it runs in the background, and when you need to wait for it to finish (and process its result), you may call Cmd.Wait()
(which will block and wait for the command to complete).
This is how it could look like:
cmd := exec.Command("npm", "install", "other_params")
cmd.Dir = entryPath
if err := cmd.Start(); err != nil {
log.Printf("Failed to start cmd: %v", err)
return
}
// Do other stuff while cmd runs in background:
log.Println("Doing other stuff...")
// And when you need to wait for the command to finish:
if err := cmd.Wait(); err != nil {
log.Printf("Cmd returned error: %v", err)
}
In contrast to Cmd.Start()
, there is Cmd.Run()
which starts the specified command and waits for it to complete, should you not need to run it in the "background". In fact Cmd.Run()
is nothing more than a chaining of Cmd.Start()
and Cmd.Wait()
calls.
Note that when running in the "background", to get the output of the app, you can't call Cmd.Output()
or Cmd.CombinedOutput()
as they run then command and get its output (and you already started the command). If you need the output of the command, set a buffer to Cmd.Stdout
which you can inspect / use after.
This is how it can be done:
cmd := exec.Command("npm", "install", "other_params")
cmd.Dir = entryPath
buf := &bytes.Buffer{}
cmd.Stdout = buf
if err := cmd.Start(); err != nil {
log.Printf("Failed to start cmd: %v", err)
return
}
// Do other stuff while cmd runs in background:
log.Println("Doing other stuff...")
// And when you need to wait for the command to finish:
if err := cmd.Wait(); err != nil {
log.Printf("Cmd returned error: %v", err)
// You may decide to continue or return here...
}
fmt.Println("[OUTPUT:]", buf.String())
If you also want to capture the standard error stream of the app, you may / have to do the same with Cmd.Stderr
. Tip: you may set the same buffer to Cmd.Stdout
and Cmd.Stderr
, and then you'll get the combined output, this is guaranteed as per doc:
If Stdout and Stderr are the same writer, and have a type that can be compared with ==,
at most one goroutine at a time will call Write.
To run a method in parallel, you can use goroutines and channels.
In goroutine, do your exec.Command
operation. This will work in background.
And you can return information using channel from your goroutine.
And finally, you can check this value sent through channel. If you check immediately after calling goroutine
, it will block until receives value from channel. So you need to check this after rest of the jobs that you want to do in parallel.
type Data struct {
output []byte
error error
}
func runCommand(ch chan<- Data) {
cmd := exec.Command("ls", "-la")
data, err := cmd.CombinedOutput()
ch <- Data{
error: err,
output: data,
}
}
func main() {
c := make(chan Data)
// This will work in background
go runCommand(c)
// Do other things here
// When everything is done, you can check your background process result
res := <-c
if res.error != nil {
fmt.Println("Failed to execute command: ", res.error)
} else {
// You will be here, runCommand has finish successfuly
fmt.Println(string(res.output))
}
}
See in action: Play Ground
Now, for OP's requirement how to implement this from another package.
From comment: Yes I understand this . but if I need to register from other package res := <-c how it should be done properly
You can try this: Play Ground. Check from another package check
Note: this link will not build
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