In this tutorial we will cover multiple methods and scenarios to execute shell commands in golang.
Run a simple shell command using exec.Command()
Here is a simple golang code which prints contents of current directory using exec.Command() function:
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("ls")
out, err := cmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
If you want to pass arguments to the command, you can include them as additional arguments to exec.Command()
. For example, to run ls -l -a
, you would use:
// you can pass multiple arguments in this format
// exec.Command("cmd", "arg1", "arg2", "argn")
cmd := exec.Command("ls", "-l")
Is it possible to execute shell command without storing output?
If you have a requirement to only execute some shell command without storing the output then we can utilize Run()
function instead of using Output()
as we used in our previous example.
package main
import (
"os/exec"
)
func main() {
// prepare the shell command
cmd := exec.Command("/bin/bash", "-c", "ls")
// Execute the shell command but don't store the output
err := cmd.Run()
if err != nil {
panic(err)
}
}
This code will not produce any output, it will just trigger the ls
command and exit.
Why we should not use exec.Command() function ?
There are multiple reasons due to which experts claim we should avoid using of exec.Command()
in Golang
- Security risks: If not properly sanitized, the arguments passed to
exec.Command
can be vulnerable to command injection attacks. - Resource usage:
exec.Command
creates a new process for each command, which can be resource-intensive and can lead to poor performance. - Limited control:
exec.Command
starts the command as a separate process and returns immediately, which means you have limited control over the command once it's running. - Error handling:
exec.Command
returns an error if the command exits with a non-zero status code, but it does not provide detailed information about the error. - Unpredictable behavior:
exec.Command
can have unexpected behavior when the command runs on different platforms or when the environment changes. - Limited Interoperability:
exec.Command
is not the best option when you need to run commands in a different shell other than the default shell.
While exec.Command
can be useful for running simple shell commands, it may not be the best option for more complex commands or when you need more control over the command execution. You may try considering using other libraries like os/exec
, exec
or even better, a library like Cobra
or Cmd
to handle command line arguments and commands in your application.
Execute a shell command in background and wait for it to complete
We have covered this scenario in depth in our previous article Start a process in background and wait Golang [4 Methods]
But for the sake of covering a scenario, here is a simple example where we wait for our sleep command to complete before exiting:
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("sleep", "10")
fmt.Println("Starting now!")
// start the command
err := cmd.Start()
if err != nil {
panic(err)
}
// wait for exec.Command to complete
err = cmd.Wait()
fmt.Println("Completed..")
if err != nil {
panic(err)
}
}
Output:
# go run main.go Starting now! Completed..
Using context to execute shell commands
We can also use the CommandContext
function of the os/exec
package which allows passing a context and passing the arguments as a slice of strings.
import (
"context"
"fmt"
"os/exec"
)
func main() {
ctx := context.Background()
cmd := exec.CommandContext(ctx, "ls", "-l", "-a")
out, err := cmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
Here you can pass multiple arguments to ctx
as in the provided format.
Output:
# go run main.go
total 28
drwxr-xr-x 3 root root 122 Jan 12 10:15 .
dr-xr-x---. 22 root root 4096 Jan 14 22:24 ..
-rw-r--r-- 1 root root 143 Jan 4 19:02 go.mod
-rw-r--r-- 1 root root 1390 Jan 4 19:02 go.sum
-rw-r--r-- 1 root root 474 Jan 7 11:23 logFile.log
-rw-r--r-- 1 root root 242 Jan 15 11:00 main.go
How to pass variable to shell commands?
We may also have a requirement to pass variable from our golang code to the shell commands as input arguments. This would require some additional handling, here are some possible methods.
Method-1: Pass variable as input argument
We can just pass the variable as input argument to exec.Command()
as shown in below example:
package main
import (
"fmt"
"os/exec"
)
func main() {
message := "Hello, World!"
cmd := exec.Command("echo", message)
out, err := cmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
Output:
# go run main.go
Hello, World!
Method-2: Using fmt.Sprintf() function
We can also use the sprintf
function to create a string with the command and the variables, and then pass the string to the Command function.
import (
"fmt"
"os/exec"
)
func main() {
message := "Hello, World!"
cmdStr := fmt.Sprintf("echo %s", message)
cmd := exec.Command("bash", "-c", cmdStr)
out, err := cmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
Output:
# go run main.go
Hello, World!
Pass integers as variables to shell commands
In the previous examples we were passing variable as string to the shell commands. But the same code cannot be used to pass an integer. Either you can convert integer to string or you can utlize golang verbs. Here is an example to pass an integer to the shell command:
package main
import (
"fmt"
"os/exec"
)
func main() {
x := 42
cmd := exec.Command("echo", fmt.Sprintf("%d", x))
out, err := cmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(out)) // 42
}
Pass floats as variables to shell commands
Here's an example of how you might use this approach to run the echo
command and pass a float variable y
to it:
package main
import (
"fmt"
"os/exec"
)
func main() {
y := 3.14
cmd := exec.Command("echo", fmt.Sprintf("%f", y))
out, err := cmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(out)) // 3.140000
}
Pass shell commands with pipe (|
) character
Method-1: Using exec.Command()
We can run shell commands with a pipe by using the Command
function of the exec
package and passing the commands as a single string separated by the pipe character "|
". Here's an example to run a simple ls
command, pipe its output to the grep
command, and search for a specific file:
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("bash", "-c", "ls | grep main.go")
out, err := cmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
We can also pass multiple commands using pipe in below format
cmd := exec.Command("bash", "-c", "command1 | command2 | command3")
Method-2: Using context package
We can achieve the same using CommandContext function of the os/exec
package which allows passing a context and passing the commands in a slice of strings.
package main
import (
"context"
"fmt"
"os/exec"
)
func main() {
ctx := context.Background()
cmd := exec.CommandContext(ctx, "bash", "-c", "ls | grep main.go")
out, err := cmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
Running multiple shell commands
Method-1: Using exec.Command() function
We can again use exec.Command()
function to provide a list of commands to get executed in sequential order.
package main
import (
"fmt"
"os/exec"
)
func main() {
commands := []string{
"ping -c 2 google.com",
"ping -c 2 facebook.com",
"ping -c 2 www.golinuxcloud.com",
}
for _, command := range commands {
cmd := exec.Command("bash", "-c", command)
out, err := cmd.Output()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(out))
}
}
Method-2: Using context function
We can also use CommandContext
function from the os/exec
package which allows passing a context and passing the commands in a slice of strings.
package main
import (
"context"
"fmt"
"os/exec"
)
func main() {
ctx := context.Background()
commands := []string{
"ping -c 2 google.com",
"ping -c 2 yahoo.com",
"ping -c 2 www.golinuxcloud.com",
}
for _, command := range commands {
cmd := exec.CommandContext(ctx, "bash", "-c", command)
out, err := cmd.Output()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(out))
}
}
Summary
In this tutorial we have tried to cover various possible methods which can be used in golang to execute a shell command. Here are some of the methods we used:
exec.Command
: This is the most commonly used method to run shell commands in Go. It creates a new process and runs the command in that process. The function takes the command and its arguments as separate parameters and returns anexec.Cmd
struct that provides methods for interacting with the command.exec.CommandContext
: it is similar toexec.Command
but it allows passing a context to the command.
We also learned how to start a process in background and wait for it to complete using Start and Wait function.
Further Reading
Hello, al lot of very good excamples, but what i missing is a how to use the exec.Commnd with spaces n the path like C:\Program Files (x86) using in a windows system. i search in a lot of websides, but i can’t find nowhere a working solution.
i try this
The output is
"\"C:\\Program Files (x86)\\mypath\myexe.exe
file does not existI say thank you for any helpful solution or information
best regards
Matthias
can you try once with
myvar := filepath.Join(os.Getenv("PROGRAMFILES(X86)"), "mypath", "myexe.exe")