exec, Cmd.Run() неправильно выполняет команду с аргументами

#windows #go #cmd

# #Windows #Вперед #cmd

Вопрос:

go версия go1.15.6 windows / amd64 dev ОС Windows [Версия 10.0.19041.630]

У меня есть приложение Go, в котором я запускаю AWS CLI с помощью exec.Cmd.Run(). Я создаю класс Cmd и заполняю аргументы.

Перед запуском Cmd я использую метод .String() для просмотра команды, которую нужно запустить. Если я беру это значение, копирую его в оболочку, команда выполняется без каких-либо изменений в выходных данных, предоставленных мне, без каких-либо сообщений о проблемах.

Однако, когда я запускаю команду, она не возвращает сообщение об ошибке. Когда я отлаживаю скрипт, он терпит неудачу, потому что он говорит, что AWS CLI сообщает, что параметр неверен.

Вопросы:

  1. Можно ли увидеть 100% необработанное представление того, что выполняется? Он не соответствует возвращаемому значению .String()
  2. Есть ли лучший способ вызвать команду уровня операционной системы, которую мне не хватает?

Реальный Пример:

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

fmt.Printf("Command: %sn", cmd.String())
// c:PROGRA~1AmazonAWSCLIV2aws.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
https://play.golang.org/p/mvV9VG8F0oz

Комментарии:

1. Обратитесь к pkg.go.dev/os/exec#Cmd

2. Мы не можем понять, что неверно в вашем формате args и awsPath когда вы не показываете, каковы они.

3. Не могли бы вы, пожалуйста, показать немного больше кода, чтобы я мог воспроизвести и разобраться в вашей ситуации? И какова ошибка, выводимая в вашей консоли?

4. @JimB, я предполагаю, что вы имеете в виду «Мы не можем определить, что неверно»? Это была моя ошибка, я думал, что предоставление выходных данных было ясным в отношении того, каковы значения. Я отредактировал свой ответ, включив в него игровую площадку go, которая демонстрирует проблемы. Однако обратите внимание, что она не может быть полностью запущена онлайн.

Ответ №1:

Из источника cmd.String:

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

Вы видите обратное, но проблема та же: при просмотре печатной командной строки не отображается точный путь к исполняемому файлу (есть ли посторонний пробел или непечатаемый символ?), То же самое с аргументами (посторонние символы?).

Используется fmt.Printf("cmd : %qn", cmd.Path) для отображения любых скрытых символов Юникода и т.д. И используйте ту же технику с каждым из аргументов.

Ответ №2:

РЕДАКТИРОВАТЬ: я нашел основную причину вашей проблемы, с которой вы столкнулись: 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
 

Поэтому, если у вас есть объявление Cmd.Path := "/usr/local/bin/aws" , вы должны объявить Cmd. Args так: Args: []string{"", "s3", "help"}, потому Args что включает команду, как Args[0] в приведенной выше ссылке на документ.
Наконец, я думаю, вы можете выполнить команду, подобную этой, для простого и эффективного:

 package main

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

func main() {
    stdout := amp;bytes.Buffer{}
    stderr := amp;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: %qn", 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"
 

Выполнено.

Комментарии:

1. могу ли я предложить использовать %q , например fmt.Printf("Command: %qn", cmd.String()) . При этом строка будет заключена в кавычки, но также будут показаны любые непечатаемые и / или неправильные символы юникода, которые могут привести к сбою API, но останутся незамеченными, если %s они используются.

2. Tks @colm.anseo, я отредактировал свой ответ и использую ваше предложение для улучшения, Tks

3. @colm.anseo Я очень ценю это! Я отметил это и буду делать это в будущем!

4. @ThoQuach Я попробую это утром и посмотрю, к чему это приведет. Я ценю вашу помощь.