star/filedate/cmd.go

205 lines
7.5 KiB
Go
Raw Normal View History

package filedate
import (
"b612.me/staros"
"fmt"
"github.com/spf13/cobra"
"os"
"path/filepath"
"regexp"
"strconv"
"time"
)
var aTime, mTime, cTime, allTime string // "2006-01-02 15:04:05" format
var fileRegexp string // 正则表达式匹配文件名
var recursive bool // 是否递归子目录
var dateFromName bool // 是否从文件名中提取日期
var dateFromNameReg string // 从文件名提取日期的正则表达式
var dateFromNameReplace string // 从文件名提取日期正则替代
var dateFromNameFormat string
var isTimestamp bool // 文件名是否是时间戳
var onlyModifyAccess bool // 只修改访问时间
var onlyModifyCreate bool // 只修改创建时间
var onlyModifyModify bool // 只修改修改时间
var excludeFolder bool // 是否排除目录本身
var Cmd = &cobra.Command{
Use: "filedate",
Short: "修改文件的创建/修改/访问时间",
Long: `修改文件的创建/修改/访问时间
可以通过 --access, --modify, --create, --all 来设置不同的时间
例如
b612 filedate file.txt --access "2023-10-01 12:00:00" --modify "2023-10-01 12:00:00"
b612 filedate dir/ --all "2023-10-01 12:00:00" -r
也可以通过 --regexp 来匹配文件名或者通过 --date-from-name 来从文件名中提取日期
比如文件名为IMG_20160711_161625.jpg,可以通过如下命令来设置时间
b612 filedate -D -y '$1$2' -x 20060102150405 -z '.*?(\d+)_(\d+).+' .\IMG_20160711_161625.jpg
`,
Run: run,
}
func init() {
Cmd.Flags().StringVarP(&aTime, "access", "a", "", "设置访问时间,格式: 2006-01-02 15:04:05")
Cmd.Flags().StringVarP(&mTime, "modify", "m", "", "设置修改时间,格式: 2006-01-02 15:04:05")
Cmd.Flags().StringVarP(&cTime, "create", "c", "", "设置创建时间,格式: 2006-01-02 15:04:05")
Cmd.Flags().StringVarP(&allTime, "all", "l", "", "设置所有时间,格式: 2006-01-02 15:04:05")
Cmd.Flags().StringVarP(&fileRegexp, "regexp", "R", "", "正则表达式匹配文件名")
Cmd.Flags().BoolVarP(&recursive, "recursive", "r", false, "递归子目录")
Cmd.Flags().BoolVarP(&dateFromName, "date-from-name", "D", false, "从文件名中提取日期")
Cmd.Flags().StringVarP(&dateFromNameReg, "date-from-name-regex", "z", "", "从文件名提取日期的正则表达式")
Cmd.Flags().StringVarP(&dateFromNameReplace, "date-from-name-replace", "y", "", "从文件名提取日期的替换表达式")
Cmd.Flags().StringVarP(&dateFromNameFormat, "date-from-name-format", "x", "2006-01-02 15:04:05", "从文件名提取日期的格式")
Cmd.Flags().BoolVarP(&isTimestamp, "timestamp", "t", false, "文件名是否是时间戳")
Cmd.Flags().BoolVarP(&onlyModifyAccess, "only-modify-access", "A", false, "只修改访问时间")
Cmd.Flags().BoolVarP(&onlyModifyCreate, "only-modify-create", "C", false, "只修改创建时间")
Cmd.Flags().BoolVarP(&onlyModifyModify, "only-modify-modify", "M", false, "只修改修改时间")
Cmd.Flags().BoolVarP(&excludeFolder, "exclude-folder", "e", false, "排除目录本身,不修改目录的时间")
}
func run(cmd *cobra.Command, args []string) {
var aDate, mDate, cDate, allDate time.Time
var err error
if aTime != "" && (onlyModifyAccess || !(onlyModifyCreate || onlyModifyModify)) {
aDate, err = time.ParseInLocation("2006-01-02 15:04:05", aTime, time.Local)
if err != nil {
cmd.PrintErrf("解析访问时间失败: %v\n", err)
return
}
}
if mTime != "" && (onlyModifyCreate || !(onlyModifyAccess || onlyModifyModify)) {
mDate, err = time.ParseInLocation("2006-01-02 15:04:05", mTime, time.Local)
if err != nil {
cmd.PrintErrf("解析修改时间失败: %v\n", err)
return
}
}
if cTime != "" && (onlyModifyCreate || !(onlyModifyAccess || onlyModifyModify)) {
cDate, err = time.ParseInLocation("2006-01-02 15:04:05", cTime, time.Local)
if err != nil {
cmd.PrintErrf("解析创建时间失败: %v\n", err)
return
}
}
if allTime != "" {
allDate, err = time.ParseInLocation("2006-01-02 15:04:05", allTime, time.Local)
if err != nil {
cmd.PrintErrf("解析所有时间失败: %v\n", err)
return
}
if mDate.IsZero() && (onlyModifyCreate || !(onlyModifyAccess || onlyModifyModify)) {
mDate = allDate
}
if cDate.IsZero() && (onlyModifyCreate || !(onlyModifyAccess || onlyModifyModify)) {
cDate = allDate
}
if aDate.IsZero() && (onlyModifyAccess || !(onlyModifyCreate || onlyModifyModify)) {
aDate = allDate
}
}
var reg, namereg *regexp.Regexp
if dateFromName {
if dateFromNameReg == "" {
cmd.PrintErrf("未指定从文件名提取日期的正则表达式\n")
return
}
if dateFromNameReplace == "" {
cmd.PrintErrf("未指定从文件名提取日期的替换表达式\n")
return
}
if dateFromNameFormat == "" {
cmd.PrintErrf("未指定从文件名提取日期的格式\n")
return
}
namereg, err = regexp.Compile(dateFromNameReg)
if err != nil {
cmd.PrintErrf("解析正则表达式失败: %v\n", err)
return
}
}
if fileRegexp != "" {
reg, err = regexp.Compile(fileRegexp)
if err != nil {
cmd.PrintErrf("解析正则表达式失败: %v\n", err)
return
}
}
for _, path := range args {
SetFileTimes(path, reg, namereg, cDate, aDate, mDate)
}
}
func SetFileTimes(path string, reg, namereg *regexp.Regexp, ctime, atime, mtime time.Time) error {
setFile := func(path string, info os.FileInfo) error {
var err error
if reg != nil && !reg.MatchString(info.Name()) {
return nil // 跳过不匹配的文件
}
if dateFromName && namereg != nil {
date := namereg.ReplaceAllString(filepath.Base(path), dateFromNameReplace)
if date == "" {
fmt.Printf("从文件名提取日期失败: %s\n", path)
return nil // 跳过无法提取日期的文件
}
var realDate time.Time
if !isTimestamp {
realDate, err = time.ParseInLocation(dateFromNameFormat, date, time.Local)
if err != nil {
fmt.Printf("解析文件名日期失败: %s, 错误: %v\n", path, err)
return nil // 跳过无法解析日期的文件
}
} else {
tmp, err := strconv.ParseInt(date, 10, 64)
if err != nil {
fmt.Printf("解析时间戳失败: %s, 错误: %v\n", path, err)
return nil // 跳过无法解析时间戳的文件
}
realDate = time.Unix(tmp, 0)
}
if onlyModifyCreate || !(onlyModifyAccess || onlyModifyModify) {
mtime = realDate
}
if onlyModifyCreate || !(onlyModifyAccess || onlyModifyModify) {
ctime = realDate
}
if onlyModifyAccess || !(onlyModifyCreate || onlyModifyModify) {
atime = realDate
}
}
if err := SetFileTime(path, ctime, atime, mtime); err != nil {
return fmt.Errorf("设置文件时间失败: %s, 错误: %v", path, err)
}
fmt.Printf("已设置文件时间: %s\n", path)
return nil
}
if !staros.Exists(path) {
return fmt.Errorf("文件不存在: %s", path)
}
if staros.IsFile(path) {
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("获取文件信息失败: %s, 错误: %v", path, err)
}
return setFile(path, info)
}
return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if staros.IsFile(path) {
return setFile(path, info)
}
if staros.IsFolder(path) {
if recursive {
err = SetFileTimes(path, reg, namereg, ctime, atime, mtime)
if err != nil {
return fmt.Errorf("递归设置文件时间失败: %s, 错误: %v", path, err)
}
}
if excludeFolder {
return nil // 如果排除目录本身,则不处理目录的时间
}
return setFile(path, info) // 处理目录本身
}
return nil
})
}