The Catcher in the Cli/CLI로 캡처
이거 뭐야?
OSS는 표준 출력, 표준 입력, 표준 오류 출력을 캡처합니다.어떤 것을 받을지 안 받을지 설정할 수 있다.
여기에 연결
유래는 샤린저의'더 캐처 인 더 리'(밀밭의 파수꾼)
공각기동대를 너무 좋아해서 소설을 싫어하는 자신도 열심히 읽는 작품이에요.
사용법
_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
합니다.분해된 물건을 매개 변수가 지정한 함수로 삶거나 구워라. 네 마음대로 해라.
CatchWithCtx
와 Catch
방법을 사용하여 이 함수를 신선도를 유지하고 외부에서 이 함수를 호출하여 사용한다.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
에서 Interval
와 Repeat
를 설정하고 반복 처리하여 버퍼가 무한히 쌓이지 않도록 합니다최후
가능하면 사용하세요.
Reference
이 문제에 관하여(The Catcher in the Cli/CLI로 캡처), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/maru44/articles/3310818d549ab4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)