Go 如何捕获标准输出,同时显示进度

Go 如何捕获标准输出,同时显示进度,go,pipe,output,stdout,goroutine,Go,Pipe,Output,Stdout,Goroutine,我有一个名为print()的函数,它每2秒打印一次数字,这个函数在goroutine中运行 我需要将它的标准输出传递给一个变量并打印它,但不能一次,直到它完成。 我需要在一个无限循环中有一个扫描仪,它将扫描标准输出并打印它,一旦功能完成,扫描仪也将完成 我试图使用它,但它不打印任何内容。 这就是我试图做的: package main import ( "bufio" "fmt" "os" "s

我有一个名为
print()
的函数,它每2秒打印一次数字,这个函数在goroutine中运行

我需要将它的标准输出传递给一个变量并打印它,但不能一次,直到它完成。
我需要在一个无限循环中有一个扫描仪,它将扫描标准输出并打印它,一旦功能完成,扫描仪也将完成

我试图使用它,但它不打印任何内容。
这就是我试图做的:

package main

import (
    "bufio"
    "fmt"
    "os"
    "sync"
    "time"
)


func print() {

    for i := 0; i < 50; i++ {
        time.Sleep(2 * time.Second)
        fmt.Printf("hello number: %d\n", i)
    }
}

func main() {
    old := os.Stdout // keep backup of the real stdout

    defer func() { os.Stdout = old }()
    r, w, _ := os.Pipe()
    os.Stdout = w

    go print()


    var wg sync.WaitGroup

    c := make(chan struct{})
    wg.Add(1)


    defer wg.Done()
    for {
        <-c
        scanner := bufio.NewScanner(r)
        for scanner.Scan() {
            m := scanner.Text()
            fmt.Println("output: " + m)
        }

    }

    c <- struct{}{}

    wg.Wait()
    fmt.Println("DONE")

}  

可以将
print()
作为“黑盒”运行并捕获其输出,尽管这有点棘手,在围棋场上不起作用

主程序包
进口(
“布菲奥”
“fmt”
“操作系统”
“运行时”
“时间”
)
func print(){
对于i:=0;i<50;i++{
时间。睡眠(100*时间。毫秒)
fmt.Printf(“你好号码:%d\n”,i)
}
}
func main(){
var ttyName字符串
如果runtime.GOOS==“windows”{
fmt.Println(“***使用'con`”)
ttyName=“con”
}否则{
fmt.Println(“***使用`/dev/tty`”)
ttyName=“/dev/tty”
}   
f、 err:=os.OpenFile(ttyName,os.O_WRONLY,0644)
如果错误!=零{
恐慌(错误)
}
延迟f.关闭()
r、 w,u:=os.Pipe()
oldStdout:=os.Stdout
os.Stdout=w
延迟函数(){
os.Stdout=旧Stdout
格式打印项次(“***完成”)
}()
fmt.Fprintln(f,“***标准输出已重定向”)
go func(){
打印()
w、 关闭()
r、 关闭()
}()
c:=make(chan结构{})

go func(){c我不知道您想做什么,但将os.Stdout分配给其他对象是错误的。如果您想在标准输出以外的其他地方打印,请使用而不是Println。您的第一个代码片段几乎没有致命问题。例如,您尝试使用
管道
扫描仪
捕获
fmt.Printf
-这可能会起作用,但然后您将捕获的结果发送到
fmt.Println()
,后者也会打印到
Stdout
,并且Stdout已经重定向到
Pipe()
,因此结果不会打印到控制台(正如您可能期望的那样),而是再次转到
Scanner
。还有其他问题,但这个问题应该首先解决。@maxim_ge我将
fmt.Println
更改为
fmt.Printf
,它仍然不起作用。我试图将
fmt.Printf
更改为
fmt.Println
,但它也不起作用:(@Peter I将
fmt.Println
更改为
fmt.Fprintf(w,“输出:+m”)
但它仍然不打印任何内容。但我需要从管道的标准输出读取它,这就是问题所在。基本上,我试图做的是在函数仍在进行时读取函数的打印。@E235我会像这样重构您的代码太棒了!它可以工作:)谢谢!感谢您理解使用
print()的重要性
作为blackbox,它对我很重要。仅供参考,它在Windows上不起作用,只在Linux上起作用,但现在它对我来说已经足够了。我需要做的下一件事是看看如何在
打印()时捕获打印
已经完成,但我会自己尝试,如果我有问题,我会打开一个新问题。我注意到,当我删除
time.Sleep(100*time.millis秒)
时,它不会打印,是否可以让扫描仪等待
打印()
function finish?@E235实际上,您必须等待扫描仪完成读取。最好的方法是以某种方式刷新标准输出,但我不知道如何进行。作为一种解决方法,您可以在
print()
之后立即添加对
time.Sleep()
的调用。这将给扫描仪一些时间
package main

import (
    "bytes"
    "fmt"
    "io"
    "os"
    "time"
)


func print() {

    for i := 0; i < 50; i++ {
        time.Sleep(2 * time.Second)
        fmt.Printf("hello number: %d\n", i)
    }
}

// https://blog.kowalczyk.info/article/wOYk/advanced-command-execution-in-go-with-osexec.html
func main() {
    old := os.Stdout // keep backup of the real stdout

    defer func() { os.Stdout = old }()
    r, w, _ := os.Pipe()
    os.Stdout = w

    go print()

    fmt.Println("DONE 1")
    outC := make(chan string)

    for {

        var buf bytes.Buffer
        io.Copy(&buf, r)
        outC <- buf.String()

        out := <-outC
        fmt.Println("output: " + out)
    }

    // back to normal state
    w.Close()


    fmt.Println("DONE")

}