Go 언어로 Tail 명령을 만들어 보세요.
91851 단어 GoGitHub ActionsCItailtech
Go 언어로 Tail 명령을 만들어 보세요.
최근 Go 언어를 배우기 시작했고, 학습의 일환으로 Go 언어로 Tail 지령을 만들어 보았다.
실제 설치부터 테스트용 유닛 테스트, GiitHub Actions의 CI/CD를 사용해 보고 싶습니다.
버전 업그레이드 등으로 인해 프로그램이 다를 수 있으니 주의하십시오
물줄기
개발 환경
Docker를 통해 Go의 개발 환경 구축
우선 지금까지 Go를 사용한 적이 없기 때문에 앞으로 Docker의 Go를 구축하는 개발 환경도 고려해야 한다.
Docker 설치
Mac 용 Docker 설치
공식 사이트에서 Docker 계정을 만들어 로그인하고 Docker Hub에서 다운로드하여 설치합니다.
설치 후 CLI를 사용하여 확인합니다.
$ docker -v
Docker version 20.10.5, build 55c4c88
버전이 이렇게 표시되면 완료됩니다.ubuntu를 사용해 보세요.
먼저 테스트 폴더를 만드는 등 Docker file을 만듭니다.
$ mkdir test
$ cd test
$ echo "From ubuntu" > Dockerfile
다음에 구축하고 셸에 들어갑니다.$ docker build -t test .
$ docker run -it test bash
$ cat /etc/os-release
잘 작동하면 이런 정보가 표시됩니다.NAME="Ubuntu"
VERSION="20.04.2 LTS (Focal Fossa)"
이렇게 하면 Docker의 동작을 확인할 수 있다.이 컨테이너는 필요 없으니 우선 캐시 삭제를 포함해라.
$ docker system prune -a
다단계 구축
이름만 물어보면 어려운 인상을 줄 수 있으니 간단하게 요약해 보면
다중 레벨 구축을 설정하기 전에 파일의 구성을 결정하십시오.
local
gotail
├── .github
│ └── workflows
│ └── go.yml
├── README.md
├── Dockerfile
├── Makefile
├── main.go
├── main_test.go
├── test.txt
├── cmd.sh
└── covercheck.sh
다음은 다단계 구축 중인 컨테이너 안의 파일 구조를 고려한다.용기1:고
stage1
go
└── src
├── main.go
├── main_test.go
├── test.txt
└── cmd.sh
컨테이너 2:alpine linux
stage2
root
├── main
├── test.txt
└── cmd.sh
상기 파일 구조의 다단계 구축을 실현하려면 다음과 같다./local/Dockerfile
FROM golang:latest
WORKDIR /go/src
COPY main.go .
COPY main_test.go .
COPY test.txt .
COPY cmd.sh .
RUN go test main_test.go main.go -v
RUN go build main.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates && \
apk add bash
WORKDIR /root
COPY /go/src/main .
COPY /go/src/test.txt .
COPY /go/src/cmd.sh .
CMD ["./cmd.sh"]
다단계 구문은 여기서 끝냅니다.자세한 내용은 공식 홈페이지를 보세요.
대략적인 스타일을 결정하다
이번에tail 명령의 기본 동작과 n 옵션의 기능을 실현하려고 합니다.
이른바 tail
파일의 마지막 줄 수를 표시하는 명령입니다. 기본적으로 10줄을 표시합니다.
tail
オプション -n
出力する行数を指定する
FIFO 알고리즘을 사용하여 - n 옵션을 실현한다.이른바 FIFO
FIFO는 First In First Out의 약칭입니다.
FIFO를 사용하면 모든 데이터를 스토리지에 저장하지 않고 파일을 읽을 수 있습니다.
비키에 상세한 기록이 있다.
이 알고리즘을 사용하여tail을 실현합니다.
사용할 라이브러리
명령 주체의 라이브러리 목록
main.go
import (
"bufio"
"flag"
"fmt"
"math"
"os"
)
테스트 라이브러리 일람main_test.go
import (
"bufio"
"fmt"
"os"
"reflect"
"strconv"
"testing"
)
우선 상상하기 편하도록 상세히 실시한다.대기열 초기화
init_queue
func init_queue() ([]string, int) {
queue := []string{}
cursor := 0
return queue, cursor
}
대열enqueue
func enqueue(queue []string, value string) []string {
queue = append(queue, value)
return queue
}
대열dequeue
func dequeue(queue []string) []string {
queue = queue[1:]
return queue
}
체크 아웃 대기열show_queue
func show_queue(queue []string, n int) []string {
if len(queue) == n {
for i := n; i > 0; i-- {
if len(queue) != 0 {
fmt.Println(queue[0])
}
queue = dequeue(queue)
}
} else {
for i := len(queue); i > 0; i-- {
if len(queue) != 0 {
fmt.Println(queue[0])
}
queue = dequeue(queue)
}
}
return queue
}
일련의 프로세스를tail로 정의tail
func tail(stream *os.File, err error, n int) []string {
queue, cursor := init_queue()
scanner := bufio.NewScanner(stream)
for scanner.Scan() {
if n < 1 {
n = int(math.Abs(float64(n)))
if n == 0 {
n = 10
}
}
queue = enqueue(queue, scanner.Text())
if n-1 < cursor {
queue = dequeue(queue)
}
cursor++
}
return queue
}
실행을 해보면 이런 느낌이 든다. ※이해하기 쉽도록'queue'를 보여 줍니다.test.txt
는 1~100의 시퀀스 번호를 한 줄 한 줄 포함하는 파일이다.bash
$ for i in `seq 100`
for> echo $i >> test.txt
main.go$ go run main.go test.txt
[1]
[1 2]
[1 2 3]
[1 2 3 4]
[1 2 3 4 5]
[1 2 3 4 5 6]
[1 2 3 4 5 6 7]
[1 2 3 4 5 6 7 8]
[1 2 3 4 5 6 7 8 9]
[1 2 3 4 5 6 7 8 9 10]
[1 2 3 4 5 6 7 8 9 10 11]
[2 3 4 5 6 7 8 9 10 11 12]
[3 4 5 6 7 8 9 10 11 12 13]
[4 5 6 7 8 9 10 11 12 13 14]
[5 6 7 8 9 10 11 12 13 14 15]
[6 7 8 9 10 11 12 13 14 15 16]
[7 8 9 10 11 12 13 14 15 16 17]
[8 9 10 11 12 13 14 15 16 17 18]
[9 10 11 12 13 14 15 16 17 18 19]
[10 11 12 13 14 15 16 17 18 19 20]
[11 12 13 14 15 16 17 18 19 20 21]
[12 13 14 15 16 17 18 19 20 21 22]
[13 14 15 16 17 18 19 20 21 22 23]
[14 15 16 17 18 19 20 21 22 23 24]
[15 16 17 18 19 20 21 22 23 24 25]
[16 17 18 19 20 21 22 23 24 25 26]
[17 18 19 20 21 22 23 24 25 26 27]
[18 19 20 21 22 23 24 25 26 27 28]
[19 20 21 22 23 24 25 26 27 28 29]
[20 21 22 23 24 25 26 27 28 29 30]
[21 22 23 24 25 26 27 28 29 30 31]
[22 23 24 25 26 27 28 29 30 31 32]
[23 24 25 26 27 28 29 30 31 32 33]
[24 25 26 27 28 29 30 31 32 33 34]
[25 26 27 28 29 30 31 32 33 34 35]
[26 27 28 29 30 31 32 33 34 35 36]
[27 28 29 30 31 32 33 34 35 36 37]
[28 29 30 31 32 33 34 35 36 37 38]
[29 30 31 32 33 34 35 36 37 38 39]
[30 31 32 33 34 35 36 37 38 39 40]
[31 32 33 34 35 36 37 38 39 40 41]
[32 33 34 35 36 37 38 39 40 41 42]
[33 34 35 36 37 38 39 40 41 42 43]
[34 35 36 37 38 39 40 41 42 43 44]
[35 36 37 38 39 40 41 42 43 44 45]
[36 37 38 39 40 41 42 43 44 45 46]
[37 38 39 40 41 42 43 44 45 46 47]
[38 39 40 41 42 43 44 45 46 47 48]
[39 40 41 42 43 44 45 46 47 48 49]
[40 41 42 43 44 45 46 47 48 49 50]
[41 42 43 44 45 46 47 48 49 50 51]
[42 43 44 45 46 47 48 49 50 51 52]
[43 44 45 46 47 48 49 50 51 52 53]
[44 45 46 47 48 49 50 51 52 53 54]
[45 46 47 48 49 50 51 52 53 54 55]
[46 47 48 49 50 51 52 53 54 55 56]
[47 48 49 50 51 52 53 54 55 56 57]
[48 49 50 51 52 53 54 55 56 57 58]
[49 50 51 52 53 54 55 56 57 58 59]
[50 51 52 53 54 55 56 57 58 59 60]
[51 52 53 54 55 56 57 58 59 60 61]
[52 53 54 55 56 57 58 59 60 61 62]
[53 54 55 56 57 58 59 60 61 62 63]
[54 55 56 57 58 59 60 61 62 63 64]
[55 56 57 58 59 60 61 62 63 64 65]
[56 57 58 59 60 61 62 63 64 65 66]
[57 58 59 60 61 62 63 64 65 66 67]
[58 59 60 61 62 63 64 65 66 67 68]
[59 60 61 62 63 64 65 66 67 68 69]
[60 61 62 63 64 65 66 67 68 69 70]
[61 62 63 64 65 66 67 68 69 70 71]
[62 63 64 65 66 67 68 69 70 71 72]
[63 64 65 66 67 68 69 70 71 72 73]
[64 65 66 67 68 69 70 71 72 73 74]
[65 66 67 68 69 70 71 72 73 74 75]
[66 67 68 69 70 71 72 73 74 75 76]
[67 68 69 70 71 72 73 74 75 76 77]
[68 69 70 71 72 73 74 75 76 77 78]
[69 70 71 72 73 74 75 76 77 78 79]
[70 71 72 73 74 75 76 77 78 79 80]
[71 72 73 74 75 76 77 78 79 80 81]
[72 73 74 75 76 77 78 79 80 81 82]
[73 74 75 76 77 78 79 80 81 82 83]
[74 75 76 77 78 79 80 81 82 83 84]
[75 76 77 78 79 80 81 82 83 84 85]
[76 77 78 79 80 81 82 83 84 85 86]
[77 78 79 80 81 82 83 84 85 86 87]
[78 79 80 81 82 83 84 85 86 87 88]
[79 80 81 82 83 84 85 86 87 88 89]
[80 81 82 83 84 85 86 87 88 89 90]
[81 82 83 84 85 86 87 88 89 90 91]
[82 83 84 85 86 87 88 89 90 91 92]
[83 84 85 86 87 88 89 90 91 92 93]
[84 85 86 87 88 89 90 91 92 93 94]
[85 86 87 88 89 90 91 92 93 94 95]
[86 87 88 89 90 91 92 93 94 95 96]
[87 88 89 90 91 92 93 94 95 96 97]
[88 89 90 91 92 93 94 95 96 97 98]
[89 90 91 92 93 94 95 96 97 98 99]
[90 91 92 93 94 95 96 97 98 99 100]
91
92
93
94
95
96
97
98
99
100
다음에 기억이 나면 팩스로 할게요.가능한 한 함수를 총결하다.
tail
func tail(stream *os.File, n int) []string {
queue := []string{}
scanner := bufio.NewScanner(stream)
for scanner.Scan() {
queue = append(queue, scanner.Text())
if n <= len(queue)-1 {
queue = queue[1:]
}
}
return queue
}
func show(queues []string) {
for _, queue := range queues {
fmt.Println(queue)
}
}
매개변수, flag 설정
main에 파일 읽기, 표준 입력 읽기, 옵션 표시를 지우는 처리를 씁니다.
여러 개의 파일을 읽어야 하기 때문에 그 처리도 써야 한다.
main
func main() {
const USAGE string = "Usage: gotail [-n #] [file]"
intOpt := flag.Int("n", 10, USAGE)
flag.Usage = func() {
fmt.Println(USAGE)
}
flag.Parse()
n := int(math.Abs(float64(*intOpt)))
if flag.NArg() > 0 {
for i := 0; i < flag.NArg(); i++ {
if i > 0 {
fmt.Print("\n")
}
if flag.NArg() != 1 {
fmt.Println("==> " + flag.Arg(i) + " <==")
}
fp, err := os.Open(flag.Arg(i))
if err != nil {
fmt.Println("Error: No such file or directory")
os.Exit(1)
}
defer fp.Close()
show(tail(fp, n))
}
} else {
show(tail(os.Stdin, n))
}
}
단원 테스트
이어서 시험을 쓴다.
여기에 미리 준비한
test.txt
을 읽어 검증한다.또한 파일에 대해 고려할 수 있는 옵션을 테스트할 수 있습니다.
이번
test.txt
은 100줄의 번호 숫자로 -n 1 ~ -n 100
까지 순서대로 테스트했다.testTail
func TestTail(t *testing.T) {
var actual_all []string
var expected_all []string
count := 0
fp, err := os.Open("./test.txt")
if err != nil {
fmt.Println("Error: No such file or directory")
os.Exit(1)
}
defer fp.Close()
scanner := bufio.NewScanner(fp)
for scanner.Scan() {
count++
}
n_all := 1
for i := count; i > 0; i-- {
fp, err = os.Open("./test.txt")
actual_all = tail(fp, n_all)
expected_all = append([]string{strconv.Itoa(i)}, expected_all...)
if reflect.DeepEqual(actual_all, expected_all) {
t.Log(reflect.DeepEqual(actual_all, expected_all))
} else {
t.Errorf("got %v\nwant %v", actual_all, expected_all)
}
n_all++
}
}
테스트 덮어쓰기 확인
또한 테스트 덮어쓰기를 확인합니다.
다음 명령을 통해 확인할 수 있습니다.
사용자가 사용할 수 있는 자원이 제한될 수 있습니다
$ ulimit -a
를 통해 확인합니다.필요에 따라 집행$ ulimit -n 500
등.testcover
$ go test main_test.go main.go -coverprofile=cover.out
$ go tool cover -html=cover.out -o cover.html
$ open cover.html
이런 식으로 확인할 수 있다.80퍼센트 정도 넘었기 때문에 계속 전진했다.
docker를 시작하여build을 실행합니다.
방금 Docker file의 설정에 따라test의 구현도 진행됩니다.
cmd.sh에서 Stage2에서 실행하고자 하는 명령을 기술합니다.
cmd.sh
#!/bin/sh -eux
./main < test.txt
기술을 완성한 후 컨테이너를 가동하다.하나하나 지령을 내리는 것은 번거롭기 때문에 Makefile로 지령을 간소화합니다.
Makefile
NAME := gotail
.PHONY: all
all: docker-build docker-run
.PHONY: docker-build
docker-build:
docker build -t $(NAME) .
.PHONY: docker-run
docker-run:
docker run --rm $(NAME)
$ make
이렇게 되면 일련의 집행을 확인하면 OK.GiitHub Action 기반 CI/CD(Go, Docker)
GiitHub Actions에서는 GiitHub에서 테스트를 뛸 수도 있고 build를 할 수도 있어 매우 편리하다.
이번에는 고 단일 검증 동작과 Docker를 시작하는 동작을 시도해 봅시다.
먼저 다음 디렉토리 구조로 미리 설정해야 합니다.
gotail
└─.github
└── workflows
└── go.yml
Action의 설정은 YAML 파일에 기술되어 있습니다.우선 고.yml를 기술하세요.
go.yml
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: Build
run: go build -v ./main.go
- name: Test
run: go test -v ./main_test.go main.go
대상의 지점에push와pullrequest를 실행할 때 Action이 실행되도록 설정합니다.또한build와test를 실행할 수 있습니다.
다음에도 docker를 시작해 봅시다.
gotail
└─.github
└── workflows
└── docker.yml
docker.ymlname: CI to Docker Hub
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check Out Repo
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: ./
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
- name: Run
run: make
이 시크릿은 Giithub과 Docker hub에서 설정해야 합니다.먼저 Docker Hub에 액세스합니다.
로그인이 완료되면 오른쪽 상단에 있는 아이콘을 누르고 메뉴를 표시하고 아래 항목을 누르십시오.
이어 Security→New Access Token 순으로 클릭한다.
Token을 표시하려면 title 등을 적당히 입력하고 창을 복사하고 닫으십시오.
이어서 Giithub을 방문합니다.
Setting→Secret→New repository secret을 클릭하여 방금 Token과 Docker Hub의 사용자 이름을 설정합니다.
이상 설정이 완료되었습니다.
마지막으로 창고에서push나pullrequest를 진행할 때 Action을 자동으로 실행합니다.
이번 코드 등은 이 글을 쓰는 단계에서는 아직 마스터에 통합되지 않았지만, 지허브도 올라오기 때문에 참고가 될 수 있어 미리 링크를 붙일 수 있다.
총결산
수고하셨습니다. 여기까지 읽어주셔서 감사합니다!
Go 언어는 처음 사용해 Go 언어의 장점으로 자주 열거되는데, 초보자라도 쉽게 이해할 수 있고 처리 속도가 빠르고 코드가 적으며 라이브러리가 풍부해 병행 처리 가능성이 높고 안전성이 높다는 느낌이 확실히 든다.
다음은 간단한 응용 프로그램 제작과 병행 처리를 시도해 보고 싶습니다.
Reference
이 문제에 관하여(Go 언어로 Tail 명령을 만들어 보세요.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/_kazuya/articles/f6e89eeab197d52067d7텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)