Как очистить bytes.Buffer, который установлен как StdOut в exe c. Команда в Golang (b.Reset не работает) - PullRequest
0 голосов
/ 28 января 2020

Я пытаюсь написать оболочку приложения командной строки в GoLang, которая предоставляет REST API для взаимодействия с указанным приложением и показывает его вывод.

Обычно при запуске сервера REST API он должен запускать приложение командной строки, которое продолжает работать (например, просто Powershell.exe ) и должно иметь возможность взаимодействовать с приложением и отображать его вывод по запросу через API.

Пока сервер запускается, дочерний процесс также запускается, и я могу давать команды процессу и получать его выходные данные. Для захвата выходных данных процесса я использую байт. Буфер , который я присоединяю к процессам Stdout и Stderr .

. проблема в том, что этот выходной буфер кажется бесконечным, и его невозможно очистить. Запуск « .Reset () » в этом буфере, похоже, полностью игнорируется, и всегда отображается полный буферный вывод.

Вот код:

package main

import (
    "bytes"
    "fmt"
    "log"
    "net/http"
    "os/exec"
)

func main() {

    var c = exec.Command("powershell.exe")
    var b bytes.Buffer
    c.Stdout = &b
    c.Stderr = &b

    stdin, err := c.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Do stuff")
    })

    http.HandleFunc("/clear", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Cleared")
        b.Reset()
    })

    http.HandleFunc("/write", func(w http.ResponseWriter, r *http.Request) {
        stdin.Write([]byte("echo hi\n"))
        fmt.Fprintf(w, "OK")
    })

    http.HandleFunc("/show", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, string(b.Bytes()))
    })

    fmt.Println("SERVER 1 STARTING")
    c.Start()
    c.CombinedOutput()
    http.ListenAndServe(":8081", nil)
    fmt.Println("SERVER 1 STOPPED")

}

Таким образом, ожидаемое поведение здесь заключается в том, что при вызове « / write » он выполнит «echo hi» в дочернем приложении. « / show » покажет выходной буфер приложения. и " / clear " должен очистить буфер вывода, так что при запуске " / write ", затем " / clear " и затем " / show", должна быть показана пустая строка. Но в этом случае он продолжает показывать вывод приложения, который никогда не очищается.

Интересно, что если я вручную запишу в буфер с помощью b.WriteString () , а затем сделаю b.Reset () , этот контент будет удален, но не содержимое приложения командной строки.

Есть ли способ правильно очистить байты. Буфер, когда он подключен к Stdout / Stderr? Или, в качестве альтернативы, есть ли какой-нибудь способ ограничить буфер указанным c числом строк?

1 Ответ

1 голос
/ 28 января 2020

@ JimB правильно, похоже, это проблема синхронизации. Это не веб-сервер, а то, как работает exe c .Command и как используется общий буфер.

Эта программа может помочь объяснить. Он запускает ping и читает буфер каждую секунду, пытаясь каждый раз сбрасывать буфер. Ping выполняет запись в буфер каждую секунду.

func main() {
    cmd := exec.Command("ping", "google.com")
    var buf bytes.Buffer
    cmd.Stdout = &buf

    err := cmd.Start()
    if err != nil {
        log.Fatal(err)
    }

    for i := 0; i < 3; i++ {
        time.Sleep(1 * time.Second)
        log.Println(buf.String())
        buf.Reset()
    }

    cmd.Process.Kill()
}

Но выходные данные накапливаются каждый раз, как если бы вызов функции buf.Reset () игнорировался:

$ go run main.go 
2020/01/28 22:05:25 PING google.com(syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e)) 56 data bytes
64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=1 ttl=50 time=50.3 ms

2020/01/28 22:05:26 PING google.com(syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e)) 56 data bytes
64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=1 ttl=50 time=50.3 ms
64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=2 ttl=50 time=52.0 ms

2020/01/28 22:05:27 PING google.com(syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e)) 56 data bytes
64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=1 ttl=50 time=50.3 ms
64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=2 ttl=50 time=52.0 ms
64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=3 ttl=50 time=57.1 ms

Bytes.Buffer может быть заменен синхронизированным буфером, где записи и чтения защищены мьютексом. Это немного упрощенно c, но, например:

type SyncBuf struct {
    mu  sync.Mutex
    buf bytes.Buffer
}

func (s *SyncBuf) Write(p []byte) (int, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    return s.buf.Write(p)
}

func (s *SyncBuf) Reset() {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.buf.Reset()
}

func (s *SyncBuf) String() string {
    s.mu.Lock()
    defer s.mu.Unlock()
    return s.buf.String()
}

При замене буфера вывод ведет себя как ожидалось, учитывая вызов Reset ():

$ go run main.go 
2020/01/28 22:07:03 PING google.com(syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e)) 56 data bytes
64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=1 ttl=50 time=50.4 ms

2020/01/28 22:07:04 64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=2 ttl=50 time=51.8 ms

2020/01/28 22:07:05 64 bytes from syd09s13-in-x0e.1e100.net (2404:6800:4006:807::200e): icmp_seq=3 ttl=50 time=50.8 ms

См. полный пример на детской площадке.

Отказ от ответственности: это опубликовано в качестве иллюстрации, это может быть не хорошим решением вашей проблемы.

...