How to execute shell commands in Golang? [SOLVED]


GO, GOLANG Solutions

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 an exec.Cmd struct that provides methods for interacting with the command.
  • exec.CommandContext: it is similar to exec.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

Exec a shell command in Go

 

Deepak Prasad

Deepak Prasad

He is the founder of GoLinuxCloud and brings over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels in various domains, from development to DevOps, Networking, and Security, ensuring robust and efficient solutions for diverse projects. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

2 thoughts on “How to execute shell commands in Golang? [SOLVED]”

  1. 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

      myvar :=  `"` + os.Getenv("PROGRAMFILES(X86)") + `\mypath\myexe.exe`"
      cmd := exec.Command(myvar)
      err :=  cmd.Run()
      if err != nil {
          log.Fatal(err)
      }

    The output is "\"C:\\Program Files (x86)\\mypath\myexe.exe file does not exist
    I say thank you for any helpful solution or information
    best regards
    Matthias

    Reply

Leave a Comment