【问题标题】:Capture stdout from command exec in real time实时从命令 exec 中捕获标准输出
【发布时间】:2018-01-24 01:44:09
【问题描述】:

我目前正在开发一个可以执行命令的聊天机器人。我希望它做的一件事是能够运行脚本并将脚本的标准输出输出到聊天中。

我遇到的问题是该函数收集脚本的所有标准输出并在最后返回它们,我想尝试修改它以实时编写并且在这样做时遇到了问题。

我认为这个问题可能涉及到它只有一种方法可以将文本返回到聊天频道,那就是通过重启的返回功能。但是,如果可能,我想遍历 exec 命令并输出。

这是我的代码:

func reboot(command *bot.Cmd) (string, error) {
    n := command.Args[0]
    // this return is what all gets sent into chat channel
    return runcommand(n), nil
  }


func runcommand(server string) string {
    cmd := exec.Command("/bin/bash", "-c", "python test.py %s", server)
    cmdOutput := &bytes.Buffer{}
    cmd.Stdout = cmdOutput
    err := cmd.Run()
    if err !=nil {
        os.Stderr.WriteString(err.Error())
    }
        return fmt.Sprintf(string(cmdOutput.Bytes()))
}

【问题讨论】:

  • 您需要更改reboot 来启动一个启动命令的goroutine。您可以传递通道之类的东西,或者只是传递io.Reader(例如:StdoutPipe 的输出)。这应该足以让您入门。
  • 我删除了我的答案,因为它没有解决您的问题。我尝试通过多种方式解决它,但所有不直接写入标准输出的方法似乎都缓冲了输出。
  • 尝试运行python -u test.py。默认情况下,Python 输出是缓冲的,可能会在您的情况下导致问题(特别是如果您的输出很短)。
  • @ArmanOrdookhani 成功了!谢谢!
  • @Blooze 不客气 :)

标签: go


【解决方案1】:

redis.log 的实时命令管道。 (您可以拨打bgsave进行测试。)

package main

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

func main() {
    cmd := exec.Command("tail", "-f",  "/usr/local/var/log/redis.log")

    // create a pipe for the output of the script
    cmdReader, err := cmd.StdoutPipe()
    if err != nil {
        fmt.Fprintln(os.Stderr, "Error creating StdoutPipe for Cmd", err)
        return
    }

    scanner := bufio.NewScanner(cmdReader)
    go func() {
        for scanner.Scan() {
            fmt.Printf("\t > %s\n", scanner.Text())
        }
    }()

    err = cmd.Start()
    if err != nil {
        fmt.Fprintln(os.Stderr, "Error starting Cmd", err)
        return
    }

    err = cmd.Wait()
    if err != nil {
        fmt.Fprintln(os.Stderr, "Error waiting for Cmd", err)
        return
    }
}

【讨论】:

    【解决方案2】:

    我还没有找到解决您问题的方法,但我发现了一些奇怪的东西。

    我写了三个版本的程序,以一秒的间隔重复输出一个短字符串,一个在 Python 中,一个在 C 中,一个在 Go 中:

    talker.py

    ​​>
    import time
    
    while True:
            print("Now!")
            time.sleep(1)
    

    talker.c

    #include <unistd.h>
    
    main() {
            for(;;) {
                    write(1, "Now!\n", 5);
                    sleep(1);
            }
    }
    

    talker.go

    package main
    
    import (
            "fmt"
            "time"
    )
    
    func main() {
            for {
                    fmt.Println("Now!")
                    time.Sleep(time.Second)
            }
    }
    

    当我自己运行它们时(例如python ./talker.py),它们的工作方式似乎完全相同。但是当我将输出传送到cat 时,我看到了不同; C 和 Go 版本立即将其输出显示到屏幕上,但 Python 版本并非如此。它的输出被缓冲并且在收集到足够的数据之前不会显示在屏幕上。

    我什至尝试过使用简单的 Go 版本的 cat,但这并没有改变行为:

    package main
    
    import (
            "io"
            "os"
    )
    
    func main() {
            io.Copy(os.Stdout, os.Stdin)
    }
    

    【讨论】:

    • 如果你的 Python 脚本很小并且很容易在 Go 中重写,那么它可能会起作用。
    • Python 标准输出(如果默认缓冲)。使用python -u talker.py 运行无缓冲版本。
    • @ArmanOrdookhani。谢谢,那行得通。但是为什么直接写入控制台时似乎没有缓冲呢? Python 是否检测 stdout 是否连接到控制台?
    • 我不知道一个明确的答案。我认为如果 Python 线连接到 tty(控制台),它会缓冲标准输出。我认为 C printf 是一样的,您使用了不使用 C 缓冲系统的 write 系统调用。 Go 选择在所有地方默认不缓冲。
    猜你喜欢
    • 2018-02-21
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    • 2010-09-09
    • 2011-06-28
    • 1970-01-01
    • 2017-07-09
    • 1970-01-01
    相关资源
    最近更新 更多