The Catcher in the Cli/CLI로 캡처

23772 단어 GoOSStech

이거 뭐야?


OSS는 표준 출력, 표준 입력, 표준 오류 출력을 캡처합니다.어떤 것을 받을지 안 받을지 설정할 수 있다.
여기에 연결
https://github.com/maru44/catcher-in-the-cli
유래는 샤린저의'더 캐처 인 더 리'(밀밭의 파수꾼)
공각기동대를 너무 좋아해서 소설을 싫어하는 자신도 열심히 읽는 작품이에요.

사용법


https://github.com/maru44/catcher-in-the-cli/tree/master/_sample
_sample/main.go
package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/maru44/catcher-in-the-cli"
)

func main() {
	ctx := context.Background()

	c := catcher.GenerateCatcher(
		&catcher.Settings{
			Interval: 4000,
			Repeat:   catcher.IntPtr(2),
		},
	)

	go func() {
		select {
		case <-time.After(500 * time.Millisecond):
			fmt.Println("bbb")
			fmt.Println("ccc")

			fmt.Fprintln(os.Stderr, "ddddd")
		}
	}()

	c.CatchWithCtx(ctx, writeFile)
}

func writeFile(ts []*catcher.Caught) {
	f, _ := os.OpenFile("./_sample/log.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	defer f.Close()

	for _, t := range ts {
		f.Write([]byte(t.String() + "\n"))
	}
}

우선GenerateCatcher으로 초기화하고Catch 또는CatchWithCtx 방법으로 사용한다.go run _sample/main.go의 집행 결과는 다음과 같다.
표준 출력
표준 출력
aaa
ddddd
exec: "aaa": executable file not found in $PATH
bbb
ccc
ls
LICENSE
README.md
_sample
_sample2
catcher.go
caught.go
domain.go
go.mod
tools.go

저장된 파일
log.log
Output: bbb
Output: ccc
Input: aaa
Error: ddddd
Error: exec: "aaa": executable file not found in $PATH
Output: LICENSE
Output: README.md
Output: _sample
Output: _sample2
Output: catcher.go
Output: caught.go
Output: domain.go
Output: go.mod
Output: tools.go
Input: ls

log.SetOutput()충분해요?
저도 그렇게 생각해요.
하고 싶어서 하는 거예요.
이렇게 되면 아직까지 실용적인 물건이 없어서 완성(개량하면 사용할 수 있을 것 같다)

뭐 해요?


주요 처리

context 종료, 확인stdin,stdout,stderr 및 모두 종료 후 지정한 separator로 분해catcher합니다.
분해된 물건을 매개 변수가 지정한 함수로 삶거나 구워라. 네 마음대로 해라.CatchWithCtxCatch 방법을 사용하여 이 함수를 신선도를 유지하고 외부에서 이 함수를 호출하여 사용한다.
catcher.go
func (c *catcher) catch(ctx context.Context, ch chan string, f func(cs []*Caught)) {
	c.Times++
	localCtx, cancel := context.WithTimeout(ctx, time.Millisecond*time.Duration(c.Interval))
	defer cancel()

	chOut := make(chan bool)
	chIn := make(chan bool)
	chError := make(chan bool)

	if c.OutBulk != nil {
		go c.catchStdout(localCtx, chOut)
	}
	if c.InBulk != nil {
		go c.catchStdin(localCtx, chIn)
	}
	if c.ErrorBulk != nil {
		go c.catchStderr(localCtx, chError)
	}

	for {
		select {
		case <-localCtx.Done():
			for {
				if c.IsOver(chOut, chIn, chError) {
					cs := c.Separate()
					f(cs)
					c.Reset()
					c.repeat(ch, c.Times)
					return
				}
			}
		case <-ctx.Done():
			for {
				if c.IsOver(chOut, chIn, chError) {
					cs := c.Separate()
					f(cs)
					c.Reset()
					c.repeat(ch, c.Times)
					return
				}
			}
		}
	}
}

stdout (stderr)

stderr도 대체로 같다io.Reader에서 읽은 데이터를 임시 저장용 버퍼와 표준 출력에 쓰기context가 끝나면 catcher의 필드OutBulk에 저장하고 true를 채널로 보냅니다.
복구 os.Stdout 및 종료
catcher.go
func (c *catcher) catchStdout(ctx context.Context, ch chan bool) {
	r, w, err := os.Pipe()
	if err != nil {
		panic(err)
	}
	stdout := os.Stdout
	os.Stdout = w

	for {
		select {
		case <-ctx.Done():
			w.Close()

			var buf bytes.Buffer
			mw := io.MultiWriter(stdout, &buf)
			io.Copy(mw, r)

			c.OutBulk.Text = buf.String()

			os.Stdout = stdout // restore stdout
			ch <- true
			return
		}
	}
}

stdin


bufio.Scanner를 통해 표준 입력 수신
catcher.go
func (c *catcher) catchStdin(ctx context.Context, ch chan bool) {
	scan := bufio.NewScanner(os.Stdin)

	go func() {
		select {
		case <-ctx.Done():
			ch <- true
			return
		}
	}()

	for scan.Scan() {
		c.InBulk.Text += scan.Text() + c.Separator
		com := strings.Split(scan.Text(), " ")
		out, err := exec.Command(com[0], com[1:]...).Output()
		if err != nil {
			fmt.Fprint(os.Stderr, err, c.Separator)
		} else {
			fmt.Print(string(out), c.Separator)
		}
	}
}

이런 느낌이야.catcher에서 IntervalRepeat를 설정하고 반복 처리하여 버퍼가 무한히 쌓이지 않도록 합니다

최후


가능하면 사용하세요.

좋은 웹페이지 즐겨찾기