I have a method to quickly run a shell command:
func runcmd(c string, arg ...string) (string, string, string) {
var o bytes.Buffer
var e bytes.Buffer
cmd := exec.Command(c, arg...)
cmd.Stdout = &o
cmd.Stderr = &e
err := cmd.Run()
return o.String(), e.String(), err.Error()
}
In my main have the following code:
func main() {
ver, _, exitcode := runcmd("rpm", "-q", "--queryformat", "%{VERSION}", "redhat-release")
var dist string
if exitcode != "" {
ver, _, exitcode = runcmd("rpm", "-q", "--queryformat", "%{VERSION}", "centos-release")
if exitcode != "" {
fmt.Println("Unknown OS! Exiting without running!")
os.Exit(3)
}
dist = "CentOS"
} else {
dist = "Redhat/Redhat derivative"
}
fmt.Printf("System is %s %s.\n", dist, ver)
}
Running this produces a SIGSEGV. However, when I comment the second call to runcmd it runs as normal (returning Unknown OS! Exiting without running! exit status 3). I'm new to go so i don't really understand the nil pointer dereference error to begin with, much less why it would only happen on the second call.
You are always returning err.Error(), when there is no error (err == nil) this fails with nil pointer dereference. You should do
func runcmd(c string, arg ...string) (string, string, error) {
e bytes.Buffer
cmd := exec.Command(c, arg...)
cmd.Stdout = &o
cmd.Stderr = &e
err := cmd.Run()
return o.String(), e.String(), err
}
and then
if exitcode != nil {
This is happening because of this line:
return o.String(), e.String(), err.Error()
If you are running CentOS then err := cmd.Run() will not return any error. Then err will be nil. But in return line you are returning err.Error(). You can not use access to a nil pointer. So, you are getting this error.
Related
i am trying to call a shell program using golang (os/exec) but the output i am getting is in bytes and i need to convert it into float64 but it is showing error?
error: cannot convert out (type []byte) to type float64
func Cpu_usage_data() (cpu_predict float64, err error) {
out,err1 := exec.Command("/bin/sh","data_cpu.sh").Output()
if err1 != nil {
fmt.Println(err1.Error())
}
return float64(out), err1
}
data_cpu.sh is:
top -b n 1 | egrep -w 'apache2|mysqld|php' | awk '{cpu += $9}END{print cpu/NR}'
Use bytes.Buffer and strconv.ParseFloat.
func Cpu_usage_data() (cpu_predict float64, err error) {
cmd := exec.Command("/bin/sh", "data_cpu.sh")
var out bytes.Buffer
cmd.Stdout = &out
err = cmd.Run()
if err != nil {
fmt.Println(err.Error())
}
cpu_predict, err = strconv.ParseFloat(out.String(), 64)
if err != nil {
fmt.Println(err.Error())
}
return
}
I am using Go 1.10.2 on Mac (darwin/amd64) and facing this error. It's saying no such file or directory.
Here's my code,
func loop1(gor_name string, ras_ip string) {
var a string
var c string
a = search_path()
fmt.Printf("当前路径为", a)
fmt.Println(os.Chdir(a))
c = fmt.Sprintf("%s %s %s %s", "./goreplay --input-file ", gor_name, " --input-file-loop --output-http ", ras_ip)
fmt.Printf("c: ", c)
cmd := exec.Command(c)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
channel <- 1
}
Thanks a lot for any suggestions.
The function signature for exec.Command is:
func Command(name string, args ...string) *Cmd
where name is the name of the program and args are the arguments. Try this:
cmd := exec.Command("./goreplay", "--input-file", gor_name, "--input-file-loop", "--output-http", ras_ip)
This answer is just an information on #cerise-limon answer.
The exec command requires command and then the arguments.
The string of command and args will throw the same error.
Pass the command to exec following with the comma separated args.
The other way to do is:
type Client struct {
logger *logrus.Entry
}
const shell = "/bin/bash"
// Execute executes the provided command
func (c *Client) Execute(command []string) (bool, error) {
c.logger.Info("Executing command ", shell, " -c ", strings.Join(command, " "))
output, err := exec.Command(shell, "-c", strings.Join(command, " ")).Output()
if err != nil {
return false, err
}
c.logger.Info(string(output))
return true, nil
}
func GetBashClient() *Client {
logger := logrus.NewEntry(logrus.StandardLogger())
return &Client{logger: logger}
}
Now you can call
command := []string{
"/usr/bin/<your script>.sh",
args1,
args2
}
GetBashClient().Execute(command)
in the next example, I am using nodejs to calculate 1+1 and I want the result with a same type of value, not string
example:
func main() {
cmd := exec.Command("/usr/bin/nodejs", "-p", "1+1")
var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
log.Println(err, stderr.String())
os.Exit(1)
}
fmt.Println(out.String())
}
is there way to do that?
When executing a command, you get a string back. You will need to deserialize it to get the proper type back. If you only expect your Node.js command to print numbers, you can use strconv.ParseFloat:
func main() {
cmd := exec.Command("/usr/bin/nodejs", "-p", "1+1")
b, err := cmd.Output()
v, err := strconv.ParseFloat(string(b), 64)
fmt.Println(v) // v is of type float64
}
If you want to handle more complex types such as javascript objects and array, I suggest serializing/deserializing node's result using JSON:
type Result struct {
Value float64
Args []float64
}
func main() {
var result Result
cmd := exec.Command("/usr/bin/nodejs", "-p", `JSON.stringify({"value": 1+1, "args": [1, 1]})`)
b, err := cmd.Output()
err = json.Unmarshal(b, &r)
fmt.Println(r.Value)
}
The Go documentation (http://golang.org/pkg/flag/) says:
The FlagSet type allows one to define independent sets of flags, such as to implement subcommands in a command-line interface.
I need this functionality but I can't figure out how to persuade the flag pkg to do it. When I define two FlagSets, parsing one of them will give me errors and warnings if the commandline has flags that are meant for the second one. Example:
f1 := flag.NewFlagSet("f1", flag.ContinueOnError)
apply := f1.Bool("apply", false, "")
silent := f1.Bool("silent", false, "")
if err := f1.Parse(os.Args[1:]); err == nil {
fmt.Println(*apply, *silent)
}
f2 := flag.NewFlagSet("f2", flag.ContinueOnError)
reset := f2.Bool("reset", false, "")
if err := f2.Parse(os.Args[1:]); err == nil {
fmt.Println(*reset)
}
I get all sorts of warnings if I try to do cmd -apply OR cmd -reset. I want to keep these FlagSets separate because I want to only have -silent work for -apply.
What am I missing?
You are meant to distinguish between subcommands first, and then call Parse on the right FlagSet.
f1 := flag.NewFlagSet("f1", flag.ContinueOnError)
silent := f1.Bool("silent", false, "")
f2 := flag.NewFlagSet("f2", flag.ContinueOnError)
loud := f2.Bool("loud", false, "")
switch os.Args[1] {
case "apply":
if err := f1.Parse(os.Args[2:]); err == nil {
fmt.Println("apply", *silent)
}
case "reset":
if err := f2.Parse(os.Args[2:]); err == nil {
fmt.Println("reset", *loud)
}
}
http://play.golang.org/p/eaEEx_EReX
Turns out it is possible to consume one set of flags while capturing the flags not recognized by the first set and while discarding the error messages that the flag package is prepared to emit whenever it runs into an option it doesn't recognize.
Here's one way to do it.
import (
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
)
func gentleParse(flagset *flag.FlagSet, args []string) []string {
if len(args) == 0 {
return nil
}
r := make([]string, 0, len(args))
flagset.Init(flagset.Name(), flag.ContinueOnError)
w := flagset.Output()
flagset.SetOutput(ioutil.Discard)
defer flagset.SetOutput(w)
next := args
for len(next) > 0 {
if next[0] == "--" {
r = append(r, next...)
break
}
if !strings.HasPrefix(next[0], "-") {
r, next = append(r, next[0]), next[1:]
continue
}
if err := flagset.Parse(next); err != nil {
const prefix = "flag provided but not defined: "
if strings.HasPrefix(err.Error(), prefix) {
pull := strings.TrimPrefix(err.Error(), prefix)
for next[0] != pull {
next = next[1:]
}
r, next = append(r, next[0]), next[1:]
continue
}
fmt.Fprintf(w, "%s\n", err)
flagset.SetOutput(w)
flag.Usage()
os.Exit(1)
}
next = flag.Args()
}
return r
}
Just change these code
if err := f2.Parse(os.Args[1:]); err == nil {
fmt.Println(*reset)
}
to
f2.Parse(os.Args[1:])
fmt.Println(*reset)
but the warning is just left on the console.if u wanna remove it ,modify /usr/local/go/src/flag/flag.go and recompile the golang ..
or do a copy of flag package.
→_→ 怀疑的眼神~~
Let's say I want to run 'ls' in a go program, and store the results in a string. There seems to be a few commands to fork processes in the exec and os packages, but they require file arguments for stdout, etc. Is there a way to get the output as a string?
There is an easier way now:
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
out, err := exec.Command("date").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
Where out is the standard output. It's in the format []byte, but you can change it to string easily with:
string(out)
You can also use CombinedOutput() instead of Output() which returns standard output and standard error.
exec.Command
To get both stdout and stderr into separate strings, you can use byte buffers like so:
cmd := exec.Command("date")
var outb, errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Println("out:", outb.String(), "err:", errb.String())
cmd := exec.Command("ls", "-al")
output, _ := cmd.CombinedOutput()
fmt.Println(string(output))
or
cmd := exec.Command(name, arg...)
stdout, err := cmd.StdoutPipe()
cmd.Stderr = cmd.Stdout
if err != nil {
return err
}
if err = cmd.Start(); err != nil {
return err
}
for {
tmp := make([]byte, 1024)
_, err := stdout.Read(tmp)
fmt.Print(string(tmp))
if err != nil {
break
}
}
I used this with a recent version of GO (~1.11)
// CmdExec Execute a command
func CmdExec(args ...string) (string, error) {
baseCmd := args[0]
cmdArgs := args[1:]
log.Debugf("Exec: %v", args)
cmd := exec.Command(baseCmd, cmdArgs...)
out, err := cmd.Output()
if err != nil {
return "", err
}
return string(out), nil
}
// Usage:
// out, err := CmdExec("ls", "/home")
Two options, depending on the paradigm you prefer:
os.ForkExec()
exec.Run()
Use exec.Run, passing Pipe for stdout. Read from the pipe that it returns.
If you are wanting string output, strings.Builder is more efficient [1] than
bytes.Buffer:
package main
import (
"os/exec"
"strings"
)
func main() {
c, b := exec.Command("go", "version"), new(strings.Builder)
c.Stdout = b
c.Run()
print(b.String())
}
https://golang.org/pkg/bytes#Buffer.String
Edit: This answer is obsolete. Please see Fatih Arslan's answer below.
Use exec.Run by specifying Pipe as the stdout (and stderr if you want). It will return cmd, which contains an os.File in the Stdout (and Stderr) fields. Then you can read it using for example ioutil.ReadAll.
Example:
package main
import (
"exec";
"io/ioutil";
)
func main() {
if cmd, e := exec.Run("/bin/ls", nil, nil, exec.DevNull, exec.Pipe, exec.MergeWithStdout); e == nil {
b, _ := ioutil.ReadAll(cmd.Stdout)
println("output: " + string(b))
}
}