1
コマンドからSTDOUTをフィルタリングして、\ r終端された行の連続したブロックの最初と最後の行だけを保持するようにしたい(進捗インジケータをほとんど無視する)。Golangでバイトストリームをフィルタリングする正しい方法はありますか?
package main
import (
"bytes"
"fmt"
"os/exec"
)
var cr = []byte("\r")
var lf = []byte("\n")
func main() {
input1 := []byte("a\nb\n\nprogress 98%\r")
input2 := []byte("progress 99%\r")
input3 := []byte("progress 100%\r")
input4 := []byte("\n\nc\n")
var stream []byte
stream = append(stream, input1...)
stream = append(stream, input2...)
stream = append(stream, input3...)
stream = append(stream, input4...)
fmt.Printf("stream:\n%s\n", stream)
streamer := &myFilter{}
streamer.Write(input1)
streamer.Write(input2)
streamer.Write(input3)
streamer.Write(input4)
final := streamer.Bytes()
fmt.Printf("streamer:\n%s\n\n", final)
cmd := exec.Command("bash", "-c", "perl -e '$|++; print qq[a\nb\n\nprogress: 98%\r]; for (99..100) { print qq[progess: $_%\r]; sleep(1); } print qq[\n\nc\n]'")
cmd.Stdout = &myFilter{}
cmd.Start()
cmd.Wait()
fromCmd := cmd.Stdout.(*myFilter).Bytes()
fmt.Printf("fromCmd:\n%s\n", fromCmd)
}
type myFilter struct {
partialLine []byte
storage []byte
}
func (w *myFilter) Write(p []byte) (n int, err error) {
// in order to filter out all but the first and last line of a set of \r
// terminated lines (a progress bar), we need to collect whole \n terminated
// lines
lines := bytes.SplitAfter(p, lf)
if len(w.partialLine) > 0 || (len(lines) == 1 && !bytes.HasSuffix(p, lf)) {
w.partialLine = append(w.partialLine, lines[0]...)
partialComplete := false
if len(lines) > 1 {
lines = lines[1:]
partialComplete = true
} else {
lines = nil
if bytes.HasSuffix(p, lf) {
partialComplete = true
}
}
if partialComplete {
w.filterCR(w.partialLine)
w.partialLine = nil
}
}
lastLineIndex := len(lines) - 1
if lastLineIndex > -1 && !bytes.HasSuffix(p, lf) {
w.partialLine, lines = lines[lastLineIndex], lines[:lastLineIndex]
}
for _, line := range lines {
w.filterCR(line)
}
return len(p), nil
}
func (w *myFilter) filterCR(p []byte) {
if bytes.Contains(p, cr) {
lines := bytes.Split(p, cr)
w.store(lines[0])
w.store(lf)
if len(lines) > 2 {
w.store(lines[len(lines)-2])
w.store(lf)
}
} else {
w.store(p)
}
}
func (w *myFilter) store(p []byte) {
w.storage = append(w.storage, p...)
}
func (w *myFilter) Bytes() []byte {
if len(w.partialLine) > 0 {
w.filterCR(w.partialLine)
}
return w.storage
}
私の出力は次のとおりです:
ここは(ORIGコードは、より多くの処理を行い、これは簡易版ですが、基本的にフィルタリングは、入力が入ってくるようではない最後に、起こることがあります)私の試みです
stream:
a
b
progress 100%
c
streamer:
a
b
progress 98%
progress 100%
c
fromCmd:
a
b
ss: 100%
progess: 100%
c
「fromCmd」から出力される出力は、「ストリーマー」からの出力と一致する必要があります。
実際の出力が「壊れている」ように見えるのはどうして間違っているのですか?実際のコマンド実行は「ストリーマー」テストとは異なる動作をし、STDOUTをフィルタする良い方法は何ですか?
ありがとうございました。私はos/execのprefixSuffixSaverの(私のコピー)の一部としてこのフィルタを実行しようとしています。そして、そのコンテキストであなたのコードをどのように使用するかについてはあまりよく分かりません。 – sbs
@sbs:私が言ったのは、スキャナーを使って、 'myFilter'の_instead_をバッファすることです。これには行全体をバッファリングする必要がありますが、実装には同じ制限があります。 – JimB