按日期将文件分类到子文件夹中 - bash

5
基本上,我的硬盘崩溃了,我能够恢复所有文件,但是,所有文件保留了它们的元数据和一些保留了它们的名称,我有274000张图片,我需要将它们或多或少地按日期分类到文件夹中。
所以假设从第一个文件开始,它会从文件中获取日期,创建一个子文件夹,并在日期发生变化之前不断将该文件移动到创建的文件夹中,一旦日期发生变化,它会创建一个新文件夹并继续执行相同的操作。
我相信这是可能的,我真的不想手动执行此操作,因为这需要几个星期...
假设我有一个目标文件夹/target/。
目标包含274000个文件,没有任何子文件夹。
文件夹结构应为 /target/YY/DD_MM/filenames。
我想为此创建一个bash脚本,但我真的不确定该从哪里开始。
我找到了这个:
#!/bin/bash

DIR=/home/data
target=$DIR
cd "$DIR"

for file in *; do

    dname="$( date -d "${file%-*}" "+$target/%Y/%b_%m" )"
    mkdir -vp "${dname%/*}"
    mv -vt "$dname" "$file"

done

没有检查文件夹是否存在,创建一个文件夹会删除该文件夹内的文件吗?

我也不确定在目录路径名中添加星号会做什么?

我对bash不是很熟悉,但如果有人能向我解释一下正在发生什么,我会很高兴让这个工作起来。

谢谢!


这些文件的名称中是否包含日期?请给我们提供一些输入文件的样例名称。 - Gilles Quénot
不是全部都不能,我希望有一种方法可以从文件本身获取日期 @sputnick - Shannon Hochkins
ls -ltr 显示的文件日期不同吗? - Gilles Quénot
在Linux中,文件的创建日期通常不会被编码。您可以获取文件的访问日期和修改日期。您更喜欢按哪个日期进行排序? - repzero
@Xorg,我刚在这个帖子上发布了一个答案,你介意检查一下以确保我没有犯任何错误吗? - Shannon Hochkins
抱歉,@Shannon Hochkins。 - repzero
3个回答

6

我似乎找到了一个适合我的答案,这在OSX上对三个文件有效,在我运行它之前,可以请你们检查一下,这不会在某些地方失败吗?

#!/bin/bash

DIR=/Users/limeworks/Downloads/target
target=$DIR
cd "$DIR"

for file in *; do
    # Top tear folder name
    year=$(stat -f "%Sm" -t "%Y" $file)
    # Secondary folder name
    subfolderName=$(stat -f "%Sm" -t "%d-%m-%Y" $file)

    if [ ! -d "$target/$year" ]; then
        mkdir "$target/$year"
        echo "starting new year: $year"
    fi
    if [ ! -d "$target/$year/$subfolderName" ]; then
        mkdir "$target/$year/$subfolderName"
        echo "starting new day & month folder: $subfolderName"
    fi
    echo "moving file $file"
    mv "$file" "$target/$year/$subfolderName"

done

我注意到 "stat -f "%Sm" -t "%d-%m-%Y" $file" 的输出结果不是以人类可读的方式呈现。每个文件的输出样本都像这样 "32ac003f03444607 255 ef53 4096 4096 16788382 12900871 12042299 4276224 39"。这样可以吗? - repzero
1
你在使用OSX吗?对我来说它没有起作用 :/ - Shannon Hochkins
@ShannonHochkins:你最终使用了什么? - G. Deward
在这个非答案中,Matt Danihy发表评论:回答很好,但是对于带有空格的文件名存在问题。没有空格时可以正常排序,有空格时会将所有内容转储到目标文件夹的根目录中。 - Petter Friberg
Matt - 在我的观点中,文件路径中不需要也没有任何空格,这个逻辑可以扩展到你身上,并且与此OP无关。 - Shannon Hochkins
如果文件名包含空格,会遇到麻烦。因此,请将"$file"替换为"$(echo $file)"。 - mediter

4

由于我的文件系统是远程挂载的,访问时间很长,因此其他解决方案的性能存在问题。

我已经开发了一些改进的bash和python解决方案:


Bash版本:

record # cat test.sh
for each in *.mkv
do
  date=$(date +%Y-%d-%m -r "$each");
  _DATES+=($date);
  FILES+=($each);
done

DATES=$(printf "%s\n" "${_DATES[@]}" | sort -u);
for date in ${DATES[@]}; do
  if [ ! -d "$date" ]; then
    mkdir "$date"
  fi
done

for i in  ${FILES[@]}; do
  dest=$(date +%Y-%d-%m -r "$i")
  mv $i $dest/$i
done

record # time bash test.sh
real    0m3.785s
record #

Python版本

import os, datetime, errno, argparse, sys

def create_file_list(CWD):
    """ takes string as path, returns tuple(files,date) """

    files_with_mtime = []
    for filename in [f for f in os.listdir(CWD) if os.path.splitext(f)[1] in ext]:
        files_with_mtime.append((filename,datetime.datetime.fromtimestamp(os.stat(filename).st_mtime).strftime('%Y-%m-%d')))
    return files_with_mtime

def create_directories(files):
    """ takes tuple(file,date) from create_file_list() """

    m = []
    for i in files:
        m.append(i[1])
    for i in set(m):
        try:
            os.makedirs(os.path.join(CWD,i))
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise

def move_files_to_folders(files):
    """ gets tuple(file,date) from create_file_list() """
    for i in files:
        try:
            os.rename(os.path.join(CWD,i[0]), os.path.join(CWD,(i[1] + '/' + i[0])))
        except Exception as e:
            raise
    return len(files)


if __name__ == '__main__':

    parser = argparse.ArgumentParser(prog=sys.argv[0], usage='%(prog)s [options]')
    parser.add_argument("-e","--extension",action='append',help="File extensions to match",required=True)
    args = parser.parse_args()

    ext =  ['.' + e for e in args.extension]
    print "Moving files with extensions:", ext
    CWD = os.getcwd()
    files = create_file_list(CWD)
    create_directories(files)
    print "Moved %i files" % move_files_to_folders(files)

record # time python sort.py -e mkv
Moving files with extensions: ['.mkv']
Moved 319 files
real    0m1.543s
record #

这两个脚本都在最近3天修改过的319个mkv文件上进行了测试。


1
我刚刚尝试了Python的解决方案,对80000个JPG文件进行处理。它完美地运行了,仅耗时11秒。 - waffl

1

我写了一个小脚本并进行了测试。希望这能有所帮助。

#!/bin/bash
pwd=`pwd`
#list all files,cut date, remove duplicate, already sorted by ls.
dates=`ls  -l --time-style=long-iso|grep -e  '^-.*'|awk '{print $6}'|uniq`
#for loop to find all files modified on each unique date and copy them to your pwd
for date in $dates; do
    if [ ! -d "$date" ]; then
        mkdir "$date"
    fi
#find command will find all files modified  at particular dates and ignore hidden files.
    forward_date=`date -d  "$date + 1 day" +%F`
    find "$pwd" -maxdepth 1 -not -path '*/\.*' -type f  -newermt "$date" ! -newermt "$forward_date" -exec cp -f {} "$pwd/$date" \;
done

您必须在工作目录中,该目录包含按日期复制的文件。


1
你使用的是什么操作系统? - Shannon Hochkins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接