根据文件名将文件移动到正确的日期目录

Move files to correct date directories based on filename

我的一些文件在不正确的目录中,我正在尝试将它们移动到正确的位置。

示例:

目录 20180622 将仅包含名称中包含 20180622 的文件名

如果 20180622 目录包含名称中包含 20180623 的文件,那么它是一个放错位置的文件,应该转到相应的正确目录即 20180623 目录结构是固定的(幸好)

date1/a/b/someprefix.date1.somesuffix #no problem
date1/a/b/someprefix.date2.somesuffix # problem
date2/c/d/someprefix.date2.somesuffix # no problem
date2/e/f/someprefix.date3.somesuffix # problem

date1/a/b/someprefix.date1.somesuffix
date2/a/b/someprefix.date2.somesuffix # problem fixed
date2/c/d/someprefix.date2.somesuffix
date3/e/f/someprefix.date3.somesuffix #problem fixed

使用 find . -type f 我得到了所有文件的列表,但不知道如何 mv 文件到正确的位置。 someprefix 可以是任何东西(它也可能包含一个点,所以 cut 不是从文件名中提取日期的好方法) $f =~ (.*)(201[5-8][0-9][0-9][0-9][0-9][0-9])(.*) 是我试图从文件名中提取日期的方法

应该很容易处理,你只需要一个循环和一个if。

for path in $(find . -type f); do
    dirdate=$(echo $path | cut -d '/' -f 2)
    filedate=$(basename $path | cut -d '.' -f 2)

    if [[ $dirdate != $filedate ]]; then
        mv $path $(dirname $path | sed "s/$dirdate/$filedate/g")
    fi
done

这里的想法非常简单:它在文件中循环获取整个文件路径 (./date1/a/b/prefix.date.suffix) 并检查 date1 是否等于 date。如果不是,它将文件移动到 date1date 替换的相同路径。

编辑评论

如果你想为你的文件名处理多个分隔符,你只需要更改 filedate= 行,如:

filedate=$(basename $path | awk -F'[._\-]' '{print }' 2> /dev/null)

它有点棘手,但假设它是 Bash。

2> /dev/null 是让 awk 的警告静音。

不幸的是,使用 bash 正则表达式匹配,您无法提取 所有 子匹配项,所以我回退到 grep 来查找所有日期。

find . -type f -print0 |
  while IFS= read -d "" -r filename; do
    mapfile -t dates < <(echo "$filename" | grep -Eo '\<201[5-8][0-9]{4}\>')
    if [[ ${#dates[@]} -eq 2 ]] && [[ ${dates[0]} != ${dates[1]} ]]; then
      destdir=$(dirname "$filename" | sed "s/${dates[0]}/${dates[1]}/")
      mkdir -p "$destdir"
      mv -v "$filename" "$destdir"
    fi
  done

测试:

$ tree
.
├── 20180621
│   └── a
│       └── b
│           ├── a.20180621.txt
│           └── foo.20180701.bar
└── 20180701
    └── c
        └── d
            └── ok.20180701

6 directories, 3 files

我们有一个文件需要移动

$ find . -type f -print0 |
   while IFS= read -d "" -r filename; do
     mapfile -t dates < <(echo "$filename" | grep -Eo '\<201[5-8][0-9]{4}\>')
     if [[ ${#dates[@]} -eq 2 ]] && [[ ${dates[0]} != ${dates[1]} ]]; then
       destdir=$(dirname "$filename" | sed "s/${dates[0]}/${dates[1]}/")
       mkdir -p "$destdir"
       mv -v "$filename" "$destdir"
     fi
   done
'./20180621/a/b/foo.20180701.bar' -> './20180701/a/b/foo.20180701.bar'

结果

$ tree
.
├── 20180621
│   └── a
│       └── b
│           └── a.20180621.txt
└── 20180701
    ├── a
    │   └── b
    │       └── foo.20180701.bar
    └── c
        └── d
            └── ok.20180701

8 directories, 3 files

不依赖 grep,这是对 Arount 回答的一个调整:

find 20+([0-9])/ -type f -print0 |
    while IFS= read -d "" -r filename; do
        dirdate=${filename%%/*}
        if [[ "$(basename "$filename")" =~ 20[0-9]{6} ]]; then
            filedate=${BASH_REMATCH[0]}
            if [[ $dirdate != $filedate ]]; then
                dest=${filename/$dirdate/$filedate}
                echo mkdir -p "$(dirname "$dest")"
                echo mv -v "$filename" "$dest"
            fi
        fi
    done