For details on the complete series of tutorials, please see:
The library used to execute commands in Golang isos/exec
, the function returns aCmd
Objects, 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,NAME
The 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!