In this tutorial we will explore different methods and functions which we can use in golang to execute a process in background and then wait for it to complete.
Method-1: Using Wait() method
We can use the Start
method of the exec.Cmd
struct to start a process in the background and the Wait
method to wait for it to complete. Here's an example of how we can use these methods to start a process in the background and wait for it to complete:
package main
import (
"fmt"
"os/exec"
)
func main() {
// Start the process in the background
cmd := exec.Command("sleep", "60")
err := cmd.Start()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Process started with PID: %d\n", cmd.Process.Pid)
// Wait for the process to complete
err = cmd.Wait()
if err != nil {
fmt.Printf("Error: %v\n", err)
}
fmt.Println("Process completed.")
}
In this example the exec.Command("sleep","60")
creates a command object to execute the "sleep
" command with argument "60
", which will sleep for 60 seconds. The cmd.Start()
method is used to start the process in the background. Then the cmd.Wait()
method is used to wait for the process to complete.
Please note that the Wait
method will wait for the process to finish and it will return any error encountered while waiting. If you want to wait for the process to finish and get the process's return code, you can use Wait()
method instead of Run()
method.
Output:
# go run main.go
Process started with PID: 17458
Process completed.
Method-2: Using exec.CommandContext() function
We can also use the exec.CommandContext
function along with the context.Background()
and context.WithCancel
functions to send a process to background and then wait for it to complete.
package main
import (
"context"
"fmt"
"os/exec"
)
func main() {
// Create a background context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Start the process in the background
cmd := exec.CommandContext(ctx, "sleep", "10")
err := cmd.Start()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Process started with PID: %d\n", cmd.Process.Pid)
// Wait for the process to complete
err = cmd.Wait()
if err != nil {
fmt.Printf("Error: %v\n", err)
}
fmt.Println("Process completed.")
}
In this example, it creates a background context using context.Background()
and a cancel function using context.WithCancel(context.Background())
. The cancel function is used to cancel the context and stop the process if needed. The exec.CommandContext
function is used to create the command and pass the background context to it.
Output:
# go run main.go
Process started with PID: 18199
Process completed.
Method-3: Using goroutine
We have a gem in golang known as goroutine which can also help us achieve this magic.
package main
import (
"fmt"
"os/exec"
)
func main() {
// Start the process in a goroutine
done := make(chan error)
go func() {
cmd := exec.Command("sleep", "60")
err := cmd.Start()
if err != nil {
done <- err
return
}
fmt.Printf("Process started with PID: %d\n", cmd.Process.Pid)
done <- cmd.Wait()
}()
// Wait for the goroutine to finish
err := <-done
if err != nil {
fmt.Printf("Error: %v\n", err)
}
fmt.Println("Process completed.")
}
This example starts the process in a goroutine using the go
keyword, so the process runs in the background. Then the code creates a channel done that is used to wait for the goroutine to finish and returns any error it encountered. Then it waits for the goroutine to finish using err := <-done
and if there is any error, it prints that error.
Output:
# go run main.go Process started with PID: 18756 Process completed.
Method-4: Using sync.WaitGroup()
We can also use a sync.WaitGroup
to start a process in the background and wait for it to complete. You can read more about this function at Golang WaitGroup Complete Tutorial
package main
import (
"fmt"
"os/exec"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
cmd := exec.Command("sleep", "10")
err := cmd.Start()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Process started with PID: %d\n", cmd.Process.Pid)
err = cmd.Wait()
if err != nil {
fmt.Printf("Error: %v\n", err)
}
}()
wg.Wait()
fmt.Println("Process completed.")
}
In this example, it creates a WaitGroup variable wg
and adds 1 to it using wg.Add(1)
to indicate that there is 1 goroutine that needs to be waited on. Then it starts a goroutine using the go
keyword. Inside the goroutine, it starts the process using exec.Command("sleep", "10")
. Then it calls wg.Done()
at the end of the goroutine to indicate that the goroutine has finished. At the end of the main function, it calls wg.Wait()
which will wait until all goroutines have finished.
Output:
# go run main.go Process started with PID: 19393 Process completed.
Summary
There are many ways to start a process in background and then wait for it to complete, also get the exit status of the background process and process the return value. Here are some of the functions which we covered or which are possible:
- Using the
Start
andWait
methods of theexec.Cmd
struct. - Using the
exec.CommandContext
function along with thecontext.Background()
andcontext.WithCancel
functions. - Using the
go
keyword to start a goroutine that runs the command, and then using a channel to wait for the goroutine to finish. - Using
sync.WaitGroup
function