英文:
How to wait until a specific line is printed to os.Stderr?
问题
我正在运行一个 Goroutine,在一段延迟后,将特定的行记录到 os.Stderr
。我想要等待直到该行被记录。到目前为止,我尝试过的方法是:
package main
import (
"bufio"
"log"
"os"
"strings"
"time"
)
func main() {
go func() {
time.Sleep(time.Second)
log.Println("Hello, world!")
}()
scanner := bufio.NewScanner(os.Stderr)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "Hello, world!") {
break
}
}
}
然而,如果我运行这段代码,它会一直阻塞:
> go run main.go
2022/04/17 00:31:43 Hello, world!
这个扫描器难道不能捕获标准错误输出并执行 break
语句吗?
英文:
I'm running a Goroutine which, after some delay, logs a specific line to os.Stderr
. I'd like to wait until that line is logged. So far, what I've tried is
package main
import (
"bufio"
"log"
"os"
"strings"
"time"
)
func main() {
go func() {
time.Sleep(time.Second)
log.Println("Hello, world!")
}()
scanner := bufio.NewScanner(os.Stderr)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "Hello, world!") {
break
}
}
}
However, if I run this, it just blocks:
> go run main.go
2022/04/17 00:31:43 Hello, world!
Should this scanner not capture the standard error output and hit the break
statement?
答案1
得分: 2
如果你想拦截输出,你可以使用管道,像这样:
r, w, _ := os.Pipe()
log.SetOutput(w)
go func() {
time.Sleep(time.Second)
log.Println("Hello, world!")
}()
scanner := bufio.NewScanner(r)
https://go.dev/play/p/HdEs5tbDYDE
英文:
If you want to intercept output, you can use a pipe, like so:
r, w, _ := os.Pipe()
log.SetOutput(w)
go func() {
time.Sleep(time.Second)
log.Println("Hello, world!")
}()
scanner := bufio.NewScanner(r)
答案2
得分: 0
似乎当io.Reader
是os.Stderr
时,scanner.Scan()
会无限期地阻塞,而如果我将其设置为自定义的读取器,比如*bytes.Buffer
,它会在 Goroutine 有机会写入之前立即返回。
由于这段逻辑的上下文是一个单元测试,我通过使用assert.Eventually
来解决了这个问题:
func TestScan(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
log.SetOutput(buf)
go func() {
time.Sleep(time.Second)
log.Println("Hello, world!")
}()
assert.Eventually(t, func() bool {
scanner := bufio.NewScanner(buf)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "Hello, world!") {
return true
}
}
return false
}, 10*time.Second, 100*time.Millisecond)
}
这个测试按预期在1.1秒内通过:
> go test ./... -v
=== RUN TestScan
--- PASS: TestScan (1.10s)
PASS
(尽管如此,这并不是最高效的解决方案,因为它可能每次都会读取整个输出,但它应该能够完成工作)。
英文:
It seems that if the io.Reader
is os.Stderr
, scanner.Scan()
blocks indefinitely, whereas if I set it to a custom reader like a *bytes.Buffer
, it returns immediately before the Goroutine has a chance to write to it.
Since the context of this logic is a unit test, I worked around this by using assert.Eventually
:
func TestScan(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
log.SetOutput(buf)
go func() {
time.Sleep(time.Second)
log.Println("Hello, world!")
}()
assert.Eventually(t, func() bool {
scanner := bufio.NewScanner(buf)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "Hello, world!") {
return true
}
}
return false
}, 10*time.Second, 100*time.Millisecond)
}
This test passes in 1.1 seconds as expected:
> go test ./... -v
=== RUN TestScan
--- PASS: TestScan (1.10s)
PASS
(Granted, this is not the most efficient solution as it presumably reads the entire output each time, but it should do the job).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论