205 lines
7.5 KiB
Go
205 lines
7.5 KiB
Go
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
|
||
})
|
||
}
|