SoFunction
Updated on 2025-03-04

Detailed explanation of five ways to execute commands using os/exec in Go language

For details on the complete series of tutorials, please see:

The library used to execute commands in Golang isos/exec, the function returns aCmdObjects, according to different needs, can be divided into three situations

  • Only execute commands, no results are obtained
  • Execute the command and get the result (not distinguishing between stdout and stderr)
  • Execute the command and get the result (differing between stdout and stderr)

The first type: only execute commands, no results are obtained

Directly call the Run function of the Cmd object, only success and failure are returned, and no output results are obtained.

package main

import (
	"log"
	"os/exec"
)

func main() {
	cmd := ("ls", "-l", "/var/log/")
	err := ()
	if err != nil {
		("() failed with %s\n", err)
	}
}

The second type: execute the command and get the result

Sometimes when we execute a command, we want to get the output result, you can call Cmd's CombinedOutput function.

package main

import (
"fmt"
"log"
"os/exec"
)

func main() {
	cmd := ("ls", "-l", "/var/log/")
	out, err := ()
	if err != nil {
 ("combined out:\n%s\n", string(out))
		("() failed with %s\n", err)
	}
	("combined out:\n%s\n", string(out))
}

The CombinedOutput function returns only out and does not distinguish between stdout and stderr. If you want to distinguish them, you can look directly at the third method.

$ go run  
combined out:
total 11540876
-rw-r--r-- 2 root root  4096 Oct 29 2018 
drwx------ 2 root root  94 Nov 6 05:56 audit
-rw-r--r-- 1 root root 185249234 Nov 28 2019 message
-rw-r--r-- 2 root root 16374 Aug 28 10:13 

But before that, I found a small problem: sometimes, shell commands can be executed, but code exec can be executed.

For example, I just want to check it/var/log/What about files with the log suffix name in the directory? Students who have some Linux basics will use this command

$ ls -l /var/log/*.log
total 11540
-rw-r--r-- 2 root root  4096 Oct 29 2018 /var/log/
-rw-r--r-- 2 root root 16374 Aug 28 10:13 /var/log/

Put it in according to this method

package main

import (
"fmt"
"log"
"os/exec"
)

func main() {
	cmd := ("ls", "-l", "/var/log/*.log")
	out, err := ()
	if err != nil {
 ("combined out:\n%s\n", string(out))
		("() failed with %s\n", err)
	}
	("combined out:\n%s\n", string(out))
}

What's going on? It didn't work, it was reported as an error.

$ go run  
combined out:
ls: cannot access /var/log/*.log: No such file or directory

2020/11/11 19:46:00 () failed with exit status 2
exit status 1

Why is there an error? There is obviously no problem with the shell

It's actually very simple, it turns outls -l /var/log/*.log It is not equivalent to the following code.

("ls", "-l", "/var/log/*.log")

The shell command corresponding to the above code should be as follows. If you write this way, ls will treat the contents in the parameters as specific file names and ignore the wildcard characters.*

$ ls -l "/var/log/*.log"
ls: cannot access /var/log/*.log: No such file or directory

The third type: execute commands and distinguish between stdout and stderr

The above writing method cannot distinguish standard output from standard errors. As long as you change it to the following writing method, it can be implemented.

package main

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
)

func main() {
	cmd := ("ls", "-l", "/var/log/*.log")
	var stdout, stderr 
	 = &stdout // Standard output	 = &stderr // Standard error	err := ()
	outStr, errStr := string(()), string(())
	("out:\n%s\nerr:\n%s\n", outStr, errStr)
	if err != nil {
		("() failed with %s\n", err)
	}
}

The output is as follows, you can see that the previous error report is classified into the standard error

$ go run  
out:

err:
ls: cannot access /var/log/*.log: No such file or directory

2020/11/11 19:59:31 () failed with exit status 2
exit status 1

Fourth: Multiple command combination, please use a pipeline

Use the output result of the execution of the previous command as a parameter of the next command. Pipeline characters can be used in the shell|To achieve it.

For example, the following command counts the number of ERROR logs in the message log.

$ grep ERROR /var/log/messages | wc -l
19

Similarly, there are similar implementations in Golang.

package main
import (
 "os"
 "os/exec"
)
func main() {
 c1 := ("grep", "ERROR", "/var/log/messages")
 c2 := ("wc", "-l")
 , _ = ()
  = 
 _ = ()
 _ = ()
 _ = ()
}

The output is as follows

$ go run  
19

Fifth: Set command-level environment variables

Environment variables set using the Setenv function of the os library are used to act on the entire process's life cycle.

package main
import (
	"fmt"
	"log"
	"os"
	"os/exec"
)
func main() {
	("NAME", "wangbm")
	cmd := ("echo", ("$NAME"))
	out, err := ()
	if err != nil {
		("() failed with %s\n", err)
	}
	("%s", out)
}

As long as in this process,NAMEThe values ​​of this variable will bewangbm, no matter how many times you execute the command

$ go run  
wangbm

If you want to narrow the scope of action of environment variables to the command level, there is a way.

For the convenience of verification, I created a new sh script with the following content

$ cat /home/wangbm/
echo $NAME
$ bash /home/wangbm/ # Since there is no global environment variable NAME,So no output

In addition, the code in it is as follows

package main
import (
	"fmt"
	"os"
	"os/exec"
)


func ChangeYourCmdEnvironment(cmd * ) error {
	env := ()
	cmdEnv := []string{}

	for _, e := range env {
		cmdEnv = append(cmdEnv, e)
	}
	cmdEnv = append(cmdEnv, "NAME=wangbm")
	 = cmdEnv

	return nil
}

func main() {
	cmd1 := ("bash", "/home/wangbm/")
 ChangeYourCmdEnvironment(cmd1) // Add environment variable to cmd1 command: NAME=wangbm	out1, _ := ()
	("output: %s", out1)

	cmd2 := ("bash", "/home/wangbm/")
	out2, _ := ()
	("output: %s", out2)
}

After execution, you can see that the command executed the second time is that the variable value of the NAME is not output.

$ go run  
output: wangbm
output: 

This is the end of this article about five methods of executing commands with os/exec in Go. For more relevant content on executing commands in Go, please search for my previous articles or continue browsing the following related articles. I hope everyone will support me in the future!