如何强制关闭已打开的管道(pipe)?

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

How to force golang to close opened pipe

问题

我必须处理一个脚本的长输出并找到一些数据。这些数据很可能位于输出的开头。找到数据后,我就不需要再处理输出了,可以退出。

问题是,我无法停止处理输出,因为exec.Cmd没有任何关闭已打开命令的函数。

以下是一些简化的代码(省略了错误处理):

func processOutput(r *bufio.Reader)(){
   for {
      line, _, err := r.ReadLine()
      if 满足某个条件 {
         break
      }
      ......
   }
   return
}

func execExternalCommand(){
   cmdToExecute := "......"
   cmd := exec.Command("bash", "-c", cmdToExecute)
   output, _ := cmd.StdoutPipe()

   cmd.Start()
   r := bufio.NewReader(output)
   go processOutput(r)
   cmd.Wait()
   return
}

processOutput函数的最后,我应该怎么做才能停止cmd?也许还有其他解决方法。

谢谢。

英文:

I have to process a long output of a script and find some data. This data most likely will be located at the almost very beginning of the output. After data found I do not need to process output anymore and can quit.

The issue that I cannot stop processing output because exec.Cmd does not have any function to close opened command.

Here are some simplified code (error handling was ommited):

func processOutput(r *bufio.Reader)(){
   for {
      line, _, err := r.ReadLine()
      if some_condition_meet {
         break
      }
      ......
   }
   return
}

func execExternalCommand(){
   cmdToExecute := "......"
   cmd := exec.Command("bash", "-c", cmdToExecute)
   output, _ := cmd.StdoutPipe()

   cmd.Start()
   r := bufio.NewReader(output)
   go processOutput(r)
   cmd.Wait()
   return
}  

What should I do at the end at processOutput function to stop cmd? Maybe there is another way how to solve it.

Thanks

答案1

得分: 3

目前来看,你无法从processOutput中实现这个功能,因为它只接收一个bufio.Reader。你需要将exec.Cmd传递给它,以便它能够对分叉的进程进行操作。

要终止分叉的进程,你可以发送一个信号给它,例如:cmd.Process.Kill()cmd.Process.Signal(os.SIGINT)。请参阅exec.Cmdos.Process的文档。

英文:

As it stands, you can't do this from processOutput because all it receives is a bufio.Reader. You would need to pass the exec.Cmd to it for it to do anything with the forked process.

To kill the forked process, you can send it a signal, e.g.: cmd.Process.Kill() or cmd.Process.Signal(os.SIGINT). See documentation on exec.Cmd and os.Process.

答案2

得分: 2

你可以使用"context"来示例:

package main

import (
	"bufio"
	"context"
	"os/exec"
)

func processOutput(r *bufio.Reader, cancel context.WithCancel) {
	for {
		line, _, err := r.ReadLine()
		if some_condition_meet {
			break
		}
	}
	cancel()
	return
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	cmdToExecute := "......"
	cmd := exec.CommandContext(ctx, "bash", "-c", cmdToExecute)
	output, _ := cmd.StdoutPipe()

	cmd.Start()
	r := bufio.NewReader(output)
	go processOutput(r, cancel)
	cmd.Wait()
	return
}

如果需要在超时后结束,可以使用以下方法(示例取自这里:https://golang.org/src/os/exec/example_test.go):

func ExampleCommandContext() {
	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil {
		// 这将在100毫秒后失败。5秒的休眠将被中断。
	}
}

一个基本的示例只使用sleep,在1秒后关闭:https://play.golang.org/p/gIXKuf5Oga

英文:

You could probably use "context" for example:

<!-- language: go -->

package main

import (
	&quot;bufio&quot;
	&quot;context&quot;
	&quot;os/exec&quot;
)

func processOutput(r *bufio.Reader, cancel context.WithCancel) {
	for {
		line, _, err := r.ReadLine()
		if some_condition_meet {
			break
		}
	}
	cancel()
	return
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	cmdToExecute := &quot;......&quot;
	cmd := exec.CommandContext(ctx, &quot;bash&quot;, &quot;-c&quot;, cmdToExecute)
	output, _ := cmd.StdoutPipe()

	cmd.Start()
	r := bufio.NewReader(output)
	go processOutput(r, cancel)
	cmd.Wait()
	return
}

In case need to end with a timeout this could work (example is taken from here: https://golang.org/src/os/exec/example_test.go)

func ExampleCommandContext() {
    ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
    defer cancel()

    if err := exec.CommandContext(ctx, &quot;sleep&quot;, &quot;5&quot;).Run(); err != nil {
	// This will fail after 100 milliseconds. The 5 second sleep
	// will be interrupted.
    }
  }

A basic example just using sleep but closing it after 1 second: https://play.golang.org/p/gIXKuf5Oga

huangapple
  • 本文由 发表于 2017年8月28日 23:29:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/45922528.html
匿名

发表评论

匿名网友

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

确定