1. 新增 filebatch package:文件批量操作

2. 新增 demo/add_go_license:给 Go 仓库的所有go源码文件添加MIT许可证
3. 新增 demo/add_blog_license:给我自己博客的所有文章尾部添加声明
pull/2/head
q191201771 5 years ago
parent 28ac1f749f
commit dacff6d56b

1
.gitignore vendored

@ -10,3 +10,4 @@ tmp
/TODO.md
/pkg/tag
/pkg/taskpool

@ -32,6 +32,7 @@ pkg/ ......源码包
|-- bele/ ......提供了大小端的转换操作
|-- bininfo/ ......将编译时的 git 版本号时间Go 编译器信息打入程序中
|-- connection/ ......对 net.Conn 接口的二次封装
|-- filebatch/ ......文件批处理操作
|-- mockserver/ ......模拟一些服务端,用于快速测试其它代码
|-- mockwriter/ ......模拟 Writer 接口,用于快速测试其它代码
|-- nazajson/ ......json 操作
@ -52,3 +53,8 @@ bin/ ......可执行文件编译输出目录
naza 即哪吒(正确拼音为 nezha我女儿发音读作 naza少一个字母挺好~),希望本仓库以后能像三头六臂,有多种武器的哪吒一样,为我提供多种工具。
#### 联系我
欢迎扫码加我微信,进行技术交流或扯淡。
<img src="https://pengrl.com/images/yoko_vx.jpeg" width="180" height="180" />

@ -3,10 +3,27 @@
set -x
ROOT_DIR=`pwd`
echo ${ROOT_DIR}/bin
if [ ! -d ${ROOT_DIR}/bin ]; then
mkdir bin
fi
GitCommitLog=`git log --pretty=oneline -n 1`
# 将 log 原始字符串中的单引号替换成双引号
GitCommitLog=${GitCommitLog//\'/\"}
GitStatus=`git status -s`
BuildTime=`date +'%Y.%m.%d.%H%M%S'`
BuildGoVersion=`go version`
LDFlags=" \
-X 'github.com/q191201771/naza/pkg/bininfo.GitCommitLog=${GitCommitLog}' \
-X 'github.com/q191201771/naza/pkg/bininfo.GitStatus=${GitStatus}' \
-X 'github.com/q191201771/naza/pkg/bininfo.BuildTime=${BuildTime}' \
-X 'github.com/q191201771/naza/pkg/bininfo.BuildGoVersion=${BuildGoVersion}' \
"
cd ${ROOT_DIR}/demo/add_blog_license && go build -ldflags "$LDFlags" -o ${ROOT_DIR}/bin/add_blog_license &&
cd ${ROOT_DIR}/demo/add_go_license && go build -ldflags "$LDFlags" -o ${ROOT_DIR}/bin/add_go_license &&
ls -lrt ${ROOT_DIR}/bin &&
echo 'build done.'

@ -0,0 +1,67 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package main
import (
"bytes"
"flag"
"fmt"
"github.com/q191201771/naza/pkg/filebatch"
"github.com/q191201771/naza/pkg/nazalog"
"os"
"strings"
)
var licenseTmpl = `
> **** [https://pengrl.com/p/%s/](https://pengrl.com/p/%s/)
> **** `
func main() {
dir := parseFlag()
linesOfLicense := strings.Split(licenseTmpl, "\n")
lastLineOfLicense := linesOfLicense[len(linesOfLicense)-1]
var (
skipCount int
modCount int
)
err := filebatch.Walk(dir, true, ".md", func(path string, info os.FileInfo, content []byte) []byte {
lines := bytes.Split(content, []byte{'\n'})
if bytes.Index(lines[len(lines)-1], []byte(lastLineOfLicense)) != -1 {
skipCount++
return nil
}
var abbrlink string
for _, line := range lines {
if bytes.Index(line, []byte("abbrlink")) != -1 {
abbrlink = string(bytes.TrimSpace(bytes.Split(line, []byte{':'})[1]))
nazalog.Debug(abbrlink)
break
}
}
modCount++
license := fmt.Sprintf(licenseTmpl, abbrlink, abbrlink)
return filebatch.AddTailContent(content, []byte(license))
})
nazalog.FatalIfErrorNotNil(err)
nazalog.Infof("count. mod=%d, skip=%d", modCount, skipCount)
}
func parseFlag() string {
dir := flag.String("d", "", "dir of posts")
flag.Parse()
if *dir == "" {
flag.Usage()
os.Exit(1)
}
return *dir
}

@ -0,0 +1,77 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package main
import (
"bytes"
"flag"
"fmt"
"github.com/q191201771/naza/pkg/filebatch"
"github.com/q191201771/naza/pkg/nazalog"
"io/ioutil"
"os"
"path/filepath"
"time"
)
var licenseTmpl = `// Copyright %d, Chef. All rights reserved.
// https://%s
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
`
func main() {
dir := parseFlag()
year := time.Now().Year()
repo := achieveRepo(dir)
license := fmt.Sprintf(licenseTmpl, year, repo)
nazalog.Debug(license)
var (
skipCount int
modCount int
)
err := filebatch.Walk(dir, true, ".go", func(path string, info os.FileInfo, content []byte) []byte {
lines := bytes.Split(content, []byte{'\n'})
if bytes.Index(lines[0], []byte("Copyright")) != -1 {
skipCount++
//nc, _ := filebatch.DeleteLines(content, filebatch.LineRange{From:1, To:7})
//return nc
return nil
}
modCount++
return filebatch.AddHeadContent(content, []byte(license))
})
nazalog.FatalIfErrorNotNil(err)
nazalog.Infof("count. mod=%d, skip=%d", modCount, skipCount)
}
func achieveRepo(root string) string {
content, err := ioutil.ReadFile(filepath.Join(root, "go.mod"))
nazalog.FatalIfErrorNotNil(err)
lines := bytes.Split(content, []byte{'\n'})
repo := bytes.TrimPrefix(lines[0], []byte("module "))
return string(bytes.TrimSpace(repo))
}
func parseFlag() string {
dir := flag.String("d", "", "dir of repo")
flag.Parse()
if *dir == "" {
flag.Usage()
os.Exit(1)
}
return *dir
}

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
// package assert 提供了单元测试时的断言功能,减少一些模板代码
//
// 代码参考了 https://github.com/stretchr/testify

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package assert
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
// Package bele 提供了大小端的转换操作
//
// be是big endian的缩写即大端

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package bele
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
// Package bininfo 将编译时的git版本号时间Go编译器信息打入程序中
package bininfo

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package bininfo
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
// package connection
//
// 对 net.Conn 接口的二次封装,目的有两个:

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package connection
import (

@ -0,0 +1,129 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package filebatch
import (
"bytes"
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// @param path 带路径的文件名
// @param info 文件的 os.FileInfo 信息
// @param content 文件内容
// @return 返回nil或者content原始内容则不修改文件内容返回其他内容则会覆盖重写文件
type WalkFunc func(path string, info os.FileInfo, content []byte) []byte
// 遍历访问指定文件夹下的文件
// @param root 需要遍历访问的文件夹
// @param recursive 是否递归访问子文件夹
// @param suffix 指定文件名后缀进行过滤,如果为"",则不过滤
func Walk(root string, recursive bool, suffix string, walkFn WalkFunc) error {
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !recursive && info.IsDir() && path != root {
return filepath.SkipDir
}
if info.IsDir() {
return nil
}
if suffix != "" && !strings.HasSuffix(info.Name(), suffix) {
return nil
}
content, err := ioutil.ReadFile(path)
if err != nil {
return err
}
newContent := walkFn(path, info, content)
if newContent != nil && bytes.Compare(content, newContent) != 0 {
if err = ioutil.WriteFile(path, newContent, 0755); err != nil {
return err
}
}
return nil
})
return err
}
// 文件尾部添加内容
func AddTailContent(content []byte, tail []byte) []byte {
if !bytes.HasSuffix(content, []byte{'\n'}) {
content = append(content, '\n')
}
return append(content, tail...)
}
// 文件头部添加内容
func AddHeadContent(content []byte, head []byte) []byte {
if !bytes.HasSuffix(head, []byte{'\n'}) {
head = append(head, '\n')
}
return append(head, content...)
}
// 行号范围
// 1表示首行-1表示最后一行
type LineRange struct {
From int
To int
}
var ErrLineRange = errors.New("naza.filebatch: line range error")
func calcLineRange(len int, lr LineRange) (LineRange, error) {
// 换算成从0开始的下标
if lr.From < 0 {
lr.From = len + lr.From
} else if lr.From > 0 {
lr.From = lr.From - 1
} else {
return lr, ErrLineRange
}
if lr.To < 0 {
lr.To = len + lr.To
} else if lr.To > 0 {
lr.To = lr.To - 1
} else {
return lr, ErrLineRange
}
// 排序交换
if lr.From > lr.To {
lr.From, lr.To = lr.To, lr.From
}
if lr.From < 0 || lr.From >= len || lr.To < 0 || lr.To >= len {
return lr, ErrLineRange
}
return lr, nil
}
func DeleteLines(content []byte, lr LineRange) ([]byte, error) {
lines := bytes.Split(content, []byte{'\n'})
length := len(lines)
nlr, err := calcLineRange(length, lr)
if err != nil {
return content, err
}
var nlines [][]byte
if nlr.From > 0 {
nlines = append(nlines, lines[:nlr.From]...)
}
if nlr.To < length-1 {
nlines = append(nlines, lines[nlr.To+1:]...)
}
return bytes.Join(nlines, []byte{'\n'}), nil
}

@ -0,0 +1,260 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package filebatch
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/q191201771/naza/pkg/assert"
"github.com/q191201771/naza/pkg/nazalog"
)
var filenameToContent map[string][]byte
var head = `// Copyright %s, Chef. All rights reserved.
// https://%s
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)`
var tail = `
> author: xxx
> link: xxx
> license: xxx
`
// /<root>/
// |-- /dir1/
// |-- /dir2/
// |-- file5
// |-- file6
// |-- file7.txt
// |-- file8.txt
// |-- file1
// |-- file2
// |-- file3.txt
// |-- file4.txt
func prepareTestFile() (string, error) {
filenameToContent = make(map[string][]byte)
root, err := ioutil.TempDir("", "")
if err != nil {
return "", err
}
if root[len(root)-1] != '/' {
root = root + "/"
}
nazalog.Debugf(root)
if err = os.Mkdir(filepath.Join(root, "dir1"), 0755); err != nil {
return "", err
}
if err = os.Mkdir(filepath.Join(root, "dir2"), 0755); err != nil {
return "", err
}
filenameToContent[root+"file1"] = []byte("hello")
filenameToContent[root+"file2"] = []byte("hello")
filenameToContent[root+"file3.txt"] = []byte("hello")
filenameToContent[root+"file4.txt"] = []byte("hello")
filenameToContent[root+"dir2/file5"] = []byte("hello")
filenameToContent[root+"dir2/file6"] = []byte("hello")
filenameToContent[root+"dir2/file7.txt"] = []byte("hello")
filenameToContent[root+"dir2/file8.txt"] = []byte("hello")
for k, v := range filenameToContent {
if err = ioutil.WriteFile(k, v, 0755); err != nil {
return "", err
}
}
return root, nil
}
func testWalk(t *testing.T, recursive bool, suffix string) {
root, err := prepareTestFile()
assert.Equal(t, nil, err)
defer os.RemoveAll(root)
err = Walk(root, recursive, suffix, func(path string, info os.FileInfo, content []byte) []byte {
nazalog.Debugf("%+v %+v %s", path, info.Name(), string(content))
v := filenameToContent[path]
assert.Equal(t, v, content)
delete(filenameToContent, path)
return content
})
assert.Equal(t, nil, err)
}
func TestWalk(t *testing.T) {
testWalk(t, true, "")
assert.Equal(t, 0, len(filenameToContent))
testWalk(t, false, "")
assert.Equal(t, 4, len(filenameToContent))
testWalk(t, true, ".txt")
assert.Equal(t, 4, len(filenameToContent))
testWalk(t, false, ".txt")
assert.Equal(t, 6, len(filenameToContent))
testWalk(t, false, ".notexist")
assert.Equal(t, 8, len(filenameToContent))
}
func TestAddContent(t *testing.T) {
root, err := prepareTestFile()
assert.Equal(t, nil, err)
defer os.RemoveAll(root)
err = Walk(root, true, ".txt", func(path string, info os.FileInfo, content []byte) []byte {
lines := bytes.Split(content, []byte{'\n'})
nazalog.Debugf("%+v %d", path, len(lines))
v := filenameToContent[path]
assert.Equal(t, v, content)
delete(filenameToContent, path)
return AddHeadContent(AddTailContent(content, []byte(tail)), []byte(head))
})
assert.Equal(t, nil, err)
err = Walk(root, true, "", func(path string, info os.FileInfo, content []byte) []byte {
nazalog.Debugf("%+v %+v %s", path, info.Name(), string(content))
return nil
})
}
func TestDeleteLines(t *testing.T) {
origin := `111
222
333
444
555`
content := []byte(origin)
lines := bytes.Split(content, []byte{'\n'})
assert.Equal(t, 5, len(lines))
var (
res []byte
err error
)
// 常规操作
res, err = DeleteLines(content, LineRange{From: 1, To: 1})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`222
333
444
555`), res)
res, err = DeleteLines(content, LineRange{From: -5, To: -5})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`222
333
444
555`), res)
res, err = DeleteLines(content, LineRange{From: 2, To: 2})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
333
444
555`), res)
res, err = DeleteLines(content, LineRange{From: -4, To: -4})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
333
444
555`), res)
res, err = DeleteLines(content, LineRange{From: 4, To: 4})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
222
333
555`), res)
res, err = DeleteLines(content, LineRange{From: -2, To: -2})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
222
333
555`), res)
res, err = DeleteLines(content, LineRange{From: 5, To: 5})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
222
333
444`), res)
res, err = DeleteLines(content, LineRange{From: -1, To: -1})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
222
333
444`), res)
res, err = DeleteLines(content, LineRange{From: 1, To: 3})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`444
555`), res)
res, err = DeleteLines(content, LineRange{From: -5, To: -3})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`444
555`), res)
res, err = DeleteLines(content, LineRange{From: 3, To: 5})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
222`), res)
res, err = DeleteLines(content, LineRange{From: -3, To: -1})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
222`), res)
res, err = DeleteLines(content, LineRange{From: 2, To: 4})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
555`), res)
res, err = DeleteLines(content, LineRange{From: -4, To: -2})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
555`), res)
// 非常规操作
res, err = DeleteLines(content, LineRange{From: 4, To: 2})
assert.Equal(t, nil, err)
assert.Equal(t, []byte(`111
555`), res)
res, err = DeleteLines(content, LineRange{From: 0, To: 1})
assert.Equal(t, ErrLineRange, err)
res, err = DeleteLines(content, LineRange{From: 1, To: 0})
assert.Equal(t, ErrLineRange, err)
res, err = DeleteLines(content, LineRange{From: 10, To: 20})
assert.Equal(t, ErrLineRange, err)
}

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package mockserver
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package mockserver
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package mockserver
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package mockwriter
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package mockwriter
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package nazajson
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package nazajson
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package nazalog
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
// package nazalog 日志库
package nazalog
@ -99,7 +107,7 @@ func New(modOptions ...ModOption) (Logger, error) {
if err = os.MkdirAll(l.dir, 0777); err != nil {
return nil, err
}
if l.fp, err = os.OpenFile(l.option.Filename, os.O_CREATE | os.O_WRONLY | os.O_APPEND, 0666); err != nil {
if l.fp, err = os.OpenFile(l.option.Filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666); err != nil {
return nil, err
}
}

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package nazalog
import (

@ -1,11 +1,19 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package nazastring
import "unsafe"
type sliceT struct {
array unsafe.Pointer
len int
cap int
len int
cap int
}
type stringStruct struct {

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package nazastring
import (

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
// package unique 对象唯一ID
package unique

@ -1,3 +1,11 @@
// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package unique
import (

@ -1,8 +1,10 @@
#!/usr/bin/env bash
# 在 macos 下运行 gofmt 检查
uname=$(uname)
if [[ "$uname" == "Darwin" ]]; then
# 在我的开发环境下额外做一些工作
if [[ $IS_CHEF_DEV_ENV == "true" ]]; then
echo "CHEFERASEME run add_go_license..."
add_go_license -d ./
echo "CHEFERASEME run gofmt check..."
gofiles=$(git diff --name-only --diff-filter=ACM | grep '.go$')
if [ ! -z "$gofiles" ]; then
@ -20,8 +22,6 @@ if [[ "$uname" == "Darwin" ]]; then
else
echo "CHEFERASEME mod gofiles not exist."
fi
else
echo "CHEFERASEME not run gofmt check..."
fi
# 跑 go test 生成测试覆盖率

Loading…
Cancel
Save