英文:
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.Cmd
和os.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 (
"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
}
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, "sleep", "5").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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论