我正在尝试自动化Go中的流程。我已经能够实现线程并进行相应的处理,但是输出是混合的和匹配的。
我想知道是否有一种方法可以显示程序产生的输出,并根据程序的过程显示输出。因此,如果任务A在任务B之前完成,我们将在B之前显示A的输出,反之亦然。
package main
import (
"fmt"
"log"
"os"
"os/exec"
"sync"
)
var url string
var wg sync.WaitGroup
func nikto() {
cmd := exec.Command("nikto", "-h", url)
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
wg.Done()
}
func whois() {
cmd := exec.Command("whois", "google.co")
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
wg.Done()
}
func main() {
fmt.Printf("Please input URL")
fmt.Scanln(&url)
wg.Add(1)
go nikto()
wg.Add(1)
go whois()
wg.Wait()
}
答案 0 :(得分:0)
在您的进程中,将os.Stdout
文件描述符直接传递给您调用的运行子进程的命令。这意味着子进程的STDOUT管道将直接连接到Go程序的标准输出,并且如果两个子进程同时写入,则很有可能会交错。
解决此问题的最简单方法是,您需要在Go程序中缓冲子进程的STDOUT管道的输出,以便可以拦截输出并控制何时打印。
Cmd
包中的os/exec
类型提供一个函数调用Output(),它将调用子进程并以字节片形式返回STDOUT的内容。您的代码可以轻松修改以实现此模式并处理结果,例如:
func whois() {
cmd := exec.Command("whois", "google.co")
out, err := cmd.Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(out)
wg.Done()
}
如果您使用fmt
包中的函数来打印输出,则有no guarantee不会对对fmt.Println
的并发调用进行交织。
为防止交错,您可以选择序列化对STDOUT的访问,或使用可以安全使用的记录器(例如log
包)。这是在Go流程中序列化对STDOUT的访问的示例:
package main
import (
"fmt"
"log"
"os/exec"
"sync"
)
var url string
func nikto(outChan chan<- []byte) {
cmd := exec.Command("nikto", "-h", url)
bs, err := cmd.Output()
if err != nil {
log.Fatal(err)
}
outChan <- bs
}
func whois(outChan chan<- []byte) {
cmd := exec.Command("whois", "google.com")
bs, err := cmd.Output()
if err != nil {
log.Fatal(err)
}
outChan <- bs
}
func main() {
outChan := make(chan []byte)
fmt.Printf("Please input URL")
fmt.Scanln(&url)
go nikto(outChan)
go whois(outChan)
for i := 0; i < 2; i++ {
bs := <-outChan
fmt.Println(string(bs))
}
}