diff --git a/lib/const.go b/lib/const.go index b70f874e..7230a92f 100644 --- a/lib/const.go +++ b/lib/const.go @@ -44,7 +44,9 @@ const ( OptionDisableCRC64 = "disableCRC64" OptionTimeout = "timeout" OptionInclude = "include" + OptionIncludeRegexp = "includeRegexp" OptionExclude = "exclude" + OptionExcludeRegexp = "excludeRegexp" OptionMeta = "meta" OptionRequestPayer = "payer" OptionLogLevel = "loglevel" @@ -210,7 +212,9 @@ const ( MaxTimeout = MaxInt64 DefaultNonePattern = "" IncludePrompt = "--include" + IncludeRegexpPrompt = "--include-regexp" ExcludePrompt = "--exclude" + ExcludeRegexpPrompt = "--exclude-regexp" MaxAppendObjectSize int64 = 5368709120 MaxBatchCount int = 100 ) diff --git a/lib/cp.go b/lib/cp.go index d97c1d8e..a6703f1f 100644 --- a/lib/cp.go +++ b/lib/cp.go @@ -1270,7 +1270,9 @@ var copyCommand = CopyCommand{ OptionRange, OptionEncodingType, OptionInclude, + OptionIncludeRegexp, OptionExclude, + OptionExcludeRegexp, OptionMeta, OptionACL, OptionConfigFile, @@ -1929,6 +1931,10 @@ func (cc *CopyCommand) getFileList(dpath string, chFiles chan<- fileInfoType) er } if f.IsDir() { + if !doesSingleFileMatchPatterns(fpath, cc.cpOption.filters) { + return nil + } + if fpath != dpath { if strings.HasSuffix(fileName, "\\") || strings.HasSuffix(fileName, "/") { chFiles <- fileInfoType{fileName, name} diff --git a/lib/option.go b/lib/option.go index 99f53168..9d4432fd 100644 --- a/lib/option.go +++ b/lib/option.go @@ -94,9 +94,15 @@ var OptionMap = map[string]Option{ OptionInclude: Option{"", "--include", DefaultNonePattern, OptionTypeString, "", "", fmt.Sprintf("包含对象匹配模式,如:*.jpg"), fmt.Sprintf("Include Pattern of key, e.g., *.jpg")}, + OptionIncludeRegexp: Option{"", "--include-regexp", DefaultNonePattern, OptionTypeString, "", "", + fmt.Sprintf("包含对象匹配模式,正则表达式,可包含文件夹,如:\\directory\\"), + fmt.Sprintf("Exclude Directory of key, support regex, support include path, e.g., \\directory\\")}, OptionExclude: Option{"", "--exclude", DefaultNonePattern, OptionTypeString, "", "", fmt.Sprintf("不包含对象匹配模式,如:*.txt"), fmt.Sprintf("Exclude Pattern of key, e.g., *.txt")}, + OptionExcludeRegexp: Option{"", "--exclude-regexp", DefaultNonePattern, OptionTypeString, "", "", + fmt.Sprintf("不包含对象匹配模式,正则表达式,可排除文件夹,如:\\directory\\"), + fmt.Sprintf("Exclude Directory of key, support regex, support exclude path, e.g., \\directory\\")}, OptionMeta: Option{"", "--meta", "", OptionTypeString, "", "", fmt.Sprintf("设置object的meta为[header:value#header:value...],如:Cache-Control:no-cache#Content-Encoding:gzip"), fmt.Sprintf("Set object meta as [header:value#header:value...], e.g., Cache-Control:no-cache#Content-Encoding:gzip")}, diff --git a/lib/sync.go b/lib/sync.go index 7b295542..06070818 100644 --- a/lib/sync.go +++ b/lib/sync.go @@ -385,7 +385,9 @@ var syncCommand = SyncCommand{ OptionRange, OptionEncodingType, OptionInclude, + OptionIncludeRegexp, OptionExclude, + OptionExcludeRegexp, OptionMeta, OptionACL, OptionConfigFile, diff --git a/lib/util.go b/lib/util.go index d31cf536..9f847f57 100644 --- a/lib/util.go +++ b/lib/util.go @@ -10,6 +10,7 @@ import ( "os/exec" "os/user" "path/filepath" + "regexp" "runtime" "strconv" "strings" @@ -194,7 +195,11 @@ func getFilter(cmdline []string) (bool, []filterOptionType) { filters := make([]filterOptionType, 0) for i, item := range cmdline { var strTag = "" - if strings.Index(item, IncludePrompt) == 0 { + if strings.Index(item, IncludeRegexpPrompt) == 0 { + strTag = IncludeRegexpPrompt + } else if strings.Index(item, ExcludeRegexpPrompt) == 0 { + strTag = ExcludeRegexpPrompt + } else if strings.Index(item, IncludePrompt) == 0 { strTag = IncludePrompt } else if strings.Index(item, ExcludePrompt) == 0 { strTag = ExcludePrompt @@ -252,6 +257,16 @@ func filterSingleStr(v, p string, include bool) bool { } } +func filterRegexp(v, p string, include bool) bool { + res, _ := regexp.MatchString(p, v) + + if include { + return res + } else { + return !res + } +} + func filterStrsWithInclude(vs []string, p string) []string { vsf := make([]string, 0) for _, v := range vs { @@ -283,18 +298,43 @@ func matchFiltersForStr(str string, filters []filterOptionType) bool { return true } + var fileNameFilters = make([]filterOptionType, 0) + var regexpFilters = make([]filterOptionType, 0) + for _, filter := range filters { + if strings.EqualFold(filter.name, IncludePrompt) || strings.EqualFold(filter.name, ExcludePrompt) { + fileNameFilters = append(fileNameFilters, filter) + } else if strings.EqualFold(filter.name, IncludeRegexpPrompt) || strings.EqualFold(filter.name, ExcludeRegexpPrompt) { + regexpFilters = append(regexpFilters, filter) + } + } + var res bool - if filters[0].name == IncludePrompt { - res = filterSingleStr(str, filters[0].pattern, true) + + if len(fileNameFilters) > 0 { + if filters[0].name == IncludePrompt { + res = filterSingleStr(str, fileNameFilters[0].pattern, true) + } else { + res = filterSingleStr(str, fileNameFilters[0].pattern, false) + } + + for _, filter := range fileNameFilters[1:] { + if filter.name == IncludePrompt { + res = res || filterSingleStr(str, filter.pattern, true) + } else { + res = res && filterSingleStr(str, filter.pattern, false) + } + } } else { - res = filterSingleStr(str, filters[0].pattern, false) + res = true } - for _, filter := range filters[1:] { - if filter.name == IncludePrompt { - res = res || filterSingleStr(str, filter.pattern, true) - } else { - res = res && filterSingleStr(str, filter.pattern, false) + if len(regexpFilters) > 0 { + for _, filter := range regexpFilters { + if filter.name == IncludeRegexpPrompt { + res = res || filterRegexp(str, filter.pattern, true) + } else { + res = res && filterRegexp(str, filter.pattern, false) + } } }