How to wait for the command to finish/ till some seconds in golang?

huangapple go评论80阅读模式
英文:

How to wait for the command to finish/ till some seconds in golang?

问题

我正在运行命令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)
}
英文:

I am running command exec.Command("cf api https://something.com/") and the response takes sometimes. But when executing this command, there is no wait happens but executed and goes further immediately. I need to wait for some seconds or until output has been received. How to achieve this?

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

得分: 3

首先,创建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()
英文:

First: the correct way to create the cmd is:

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

Every argument to the child program must be a separate string. This way you can also pass arguments that contain spaces in them. For instance, executing the program with:

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

will pass one command line argument to cf, which is "api https://something.com/", whereas passing two strings will pass two arguments "api" and "https://something.com/".

In your original code, you are trying to execute a program whose name is "cf api https://something.com/".

Then you can run it and get the output:

out, err:=cmd.Output()

答案2

得分: 1

这可以通过使用goroutine、channel和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("超时")
	case x := <-ch:
		fmt.Printf("程序完成;输出:%q\n", string(x.out))
		if x.err != nil {
			fmt.Printf("程序出错:%s\n", x.err)
		}
	}
}

通过在exec.Command()中选择3个选项之一,您可以看到代码以3种可能的方式运行:超时、正常子进程终止、错误的子进程终止。

使用goroutine时,通常需要注意确保它们终止,以避免资源泄漏。

还请注意,如果执行的子进程是交互式的,或者如果它将其进展打印到stdout并且重要的是在其发生时查看输出,则最好使用cmd.Run(),删除结构体,并仅在通道中报告错误。

英文:

This can be solved with a goroutine, a channel and the select statement. The sample code below also does error handling:

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

	ch := make(chan output)

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

	select {
	case &lt;-time.After(2 * time.Second):
		fmt.Println(&quot;timed out&quot;)
	case x := &lt;-ch:
		fmt.Printf(&quot;program done; out: %q\n&quot;, string(x.out))
		if x.err != nil {
			fmt.Printf(&quot;program errored: %s\n&quot;, x.err)
		}
	}
}

By choosing one of the 3 options in exec.Command(), you can see the code behaving in the 3 possible ways: timed out, normal subprocess termination, errored subprocess termination.

As usual when using goroutines, care must be taken to ensure they terminate, to avoid resource leaks.

Note also that if the executed subprocess is interactive or if it prints its progression to stdout and it is important to see the output while it is happening, then it is better to use cmd.Run(), remove the struct and report only the error in the channel.

答案3

得分: 0

你可以使用go waitGroup。这是我们将在每个goroutine中运行的函数。请注意,必须通过指针将WaitGroup传递给函数。在返回时,通知WaitGroup我们已经完成。使用Sleep函数模拟一个耗时的任务(在你的情况下可以将其删除)。这个WaitGroup用于等待在这里启动的所有goroutine完成。阻塞,直到WaitGroup计数器返回到0;所有的工作线程都通知它们已经完成。

package main

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

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

    defer wg.Done()

    fmt.Printf("Worker %d starting\n", id)

    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {

    var wg sync.WaitGroup

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

    wg.Wait()
}
英文:

You can use a go waitGroup. This is the function we’ll run in every goroutine. Note that a WaitGroup must be passed to functions by pointer. On return, notify the WaitGroup that we’re done. Sleep to simulate an expensive task. (remove it in your case) This WaitGroup is used to wait for all the goroutines launched here to finish. Block until the WaitGroup counter goes back to 0; all the workers notified they’re done.

  package main

import (
    &quot;fmt&quot;
    &quot;sync&quot;
    &quot;time&quot;
)

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

    defer wg.Done()

    fmt.Printf(&quot;Worker %d starting\n&quot;, id)

    time.Sleep(time.Second)
    fmt.Printf(&quot;Worker %d done\n&quot;, id)
}

func main() {

    var wg sync.WaitGroup

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

    wg.Wait()
}

huangapple
  • 本文由 发表于 2021年7月6日 12:44:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/68264655.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定