简体   繁体   中英

exec,Cmd.Run() does not properly run command w/arguments

go version go1.15.6 windows/amd64 dev os Windows [Version 10.0.19041.630]

I have a Go app in which I am running the AWS CLI using exec.Cmd.Run(). I build out the Cmd class and populate the arguments.

Before I run the Cmd, I use the.String() method to view the command to be ran. If I take this value, copy it to a shell, the command executes with no modifications to the output given to me with no issues reported.

However, when I run the command, it fails returning an error. When I debug the script, it is failing because it says the AWS CLI is saying a parameter is incorrect.

Questions:

  1. Is it possible to see the 100% raw representation of what is being ran? It does not match the return value of.String()
  2. Is there a better way to call an os level command that I am missing?

Real Example:

cmd := &exec.Cmd{
    Path:   awsPath,
    Args:   args,
    Stdout: &stdout,
    Stderr: &stderr,
}

fmt.Printf("Command: %s\n", cmd.String())
// c:\PROGRA~1\Amazon\AWSCLIV2\aws.exe --profile testprofile --region us-east-1 --output json ec2 describe-network-interfaces --filters Name=group-id,Values=sg-abc123
// Running above works 100% of the time if ran from a shell window

err := cmd.Run()
// always errors out saying the format is incorrect

GoPlayground Replication of Issue https://play.golang.org/p/mvV9VG8F0oz

EDIT : I have found the root cause of your problem you met: os/exec

 // Path is the path of the command to run. // // This is the only field that must be set to a non-zero // value. If Path is relative, it is evaluated relative // to Dir. Path string // Args holds command line arguments, including the command as **Args[0]**. // If the Args field is empty or nil, Run uses {Path}. // // In typical use, both Path and Args are set by calling Command. Args []string

So if you have declare the Cmd.Path:= "/usr/local/bin/aws" , you have to declare Cmd. Args Cmd. Args like this: Args: []string{"", "s3", "help"}, because the Args including the command as Args[0] in above document link. Final, I think you can exec command like this for simple and effectively:

package main

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

func main() {
    stdout := &bytes.Buffer{}
    stderr := &bytes.Buffer{}

    name := "/usr/local/bin/aws"
    arg := []string{"s3", "help"}

    cmd := exec.Command(name, arg...)
    cmd.Stderr = stderr
    cmd.Stdout = stdout

    fmt.Printf("Command: %q\n", cmd.String())

    err := cmd.Run()
    if err != nil {
        fmt.Println("Error: ", stderr.String())
    }

    fmt.Println("Output: ", stdout.String())
}
=========
$ go run main.go
Command: "/usr/local/bin/aws s3 help"

Done.

From cmd.String source:

// String returns a human-readable description of c.
// It is intended only for debugging.
// In particular, it is not suitable for use as input to a shell.
// The output of String may vary across Go releases.

You are seeing the reverse, but the problem is the same: eye-balling a printed command string does not show the exact executable path (is there a rogue space or unprintable character?), same with the arguments (rogue characters?).

Use fmt.Printf("cmd: %q\n", cmd.Path) to show any hidden unicode characters etc. And use the same technique with each of the arguments.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM