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 }) }