Как дождаться завершения команды/ до нескольких секунд в golang?

# #go

Вопрос:

Я выполняю команду exec.Command("cf api https://something.com/") , и иногда требуется ответ. Но при выполнении этой команды не происходит никакого ожидания, а выполняется и немедленно идет дальше. Мне нужно подождать несколько секунд или пока не будет получен вывод. Как этого добиться?

 func TestCMDExex(t *testing.T) {
    expectedText := "Success"
    cmd := exec.Command("cf api https://something.com/")
    cmd.Dir = "/root//"
    out, err := cmd.Output()
    if err != nil {
        t.Fail()
    }
    assert.Contains(t, string(out), expectedText)

}

 

Ответ №1:

Во-первых: правильный способ создания cmd-это:

  cmd := exec.Command("cf", "api", "https://something.com/")
 

Каждый аргумент дочерней программы должен быть отдельной строкой. Таким образом, вы также можете передавать аргументы, содержащие пробелы в них. Например, выполнение программы с:

 cmd := exec.Command("cf", "api https://something.com/")
 

передаст один аргумент командной строки cf , который является «api https://something.com/», в то время как передача двух строк приведет к передаче двух аргументов «api» и «https://something.com/».

В вашем исходном коде вы пытаетесь выполнить программу, имя которой «cf api https://something.com/».

Затем вы можете запустить его и получить результат:

 out, err:=cmd.Output()
 

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

1. Они @бурак , Спасибо! это сработало. Похоже ли это на то, что нам нужно заключать каждый текст в кавычки?

Ответ №2:

Вы можете использовать группу ожидания go. Это функция, которую мы будем запускать в каждой гороутине. Обратите внимание, что группа ожидания должна передаваться функциям по указателю. По возвращении сообщите группе ожидания, что мы закончили. Сон для имитации дорогостоящей задачи. (удалите его в вашем случае) Эта группа ожидания используется для ожидания завершения всех запущенных здесь гороутов. Блокируйте до тех пор, пока счетчик группы ожидания не вернется к 0; все работники уведомлены, что они закончили.

   package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {

    defer wg.Done()

    fmt.Printf("Worker %d startingn", id)

    time.Sleep(time.Second)
    fmt.Printf("Worker %d donen", id)
}

func main() {

    var wg sync.WaitGroup

    for i := 1; i <= 5; i   {
        wg.Add(1)
        go worker(i, amp;wg)
    }

    wg.Wait()
}
 

Ответ №3:

Это можно решить с помощью подпрограммы, канала и оператора select. Приведенный ниже пример кода также выполняет обработку ошибок:

 func main() {
    type output struct {
        out []byte
        err error
    }

    ch := make(chan output)

    go func() {
        // cmd := exec.Command("sleep", "1")
        // cmd := exec.Command("sleep", "5")
        cmd := exec.Command("false")
        out, err := cmd.CombinedOutput()
        ch <- output{out, err}
    }()

    select {
    case <-time.After(2 * time.Second):
        fmt.Println("timed out")
    case x := <-ch:
        fmt.Printf("program done; out: %qn", string(x.out))
        if x.err != nil {
            fmt.Printf("program errored: %sn", x.err)
        }
    }
}
 

Выбрав один из 3 вариантов exec.Command() , вы можете увидеть, как код ведет себя 3 возможными способами: тайм-аут, обычное завершение подпроцесса, завершение подпроцесса с ошибкой.

Как обычно при использовании goroutines, необходимо позаботиться о том, чтобы они прекратили работу, чтобы избежать утечки ресурсов.

Обратите также внимание, что если выполняемый подпроцесс является интерактивным или если он выводит свою последовательность в stdout и важно видеть вывод во время его выполнения , то лучше использовать cmd.Run() , удалить структуру и сообщать только об ошибке в канале.