|
|
// 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, err error) []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 {
|
|
|
walkFn(path, info, nil, err)
|
|
|
return nil
|
|
|
}
|
|
|
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 {
|
|
|
walkFn(path, info, content, err)
|
|
|
return nil
|
|
|
}
|
|
|
newContent := walkFn(path, info, content, nil)
|
|
|
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
|
|
|
}
|