【问题标题】:Sort files into sub folders by date - bash按日期将文件排序到子文件夹中 - bash
【发布时间】:2015-02-13 00:26:39
【问题描述】:

基本上我的硬盘崩溃了,我能够恢复所有文件,但是,所有文件都保留了它们的元数据,有些保留了它们的名称,我有 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

创建一个文件夹而不检查它是否存在会删除该文件夹中的文件吗?

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

我对 bash 不太熟悉,但如果有人可以向我解释一下发生了什么,我很乐意让它工作?

谢谢!

【问题讨论】:

  • 文件名称中是否包含日期?给我们一个示例输入文件名
  • 并非全部不,我希望有办法从它自己的文件中获取日期@sputnick
  • ls -ltr 是否显示文件的不同日期?
  • 创建日期通常不会在 linux 中的文件上编码。您可以获取文件的访问日期和修改日期。您更喜欢按哪一个日期排序?
  • @Xorg 我刚刚在这个线程上发布了一个答案,你介意检查一下以确保我没有犯任何错误吗?

标签: bash sorting command-line directory command


【解决方案1】:

由于我的文件系统是远程安装的并且访问时间很长,因此我遇到了其他解决方案的性能问题。

我在 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 version:

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 文件上进行了测试。

【讨论】:

  • 我刚刚在一个包含 80000 个 jpg 文件的文件夹上尝试了 python 解决方案。它运行良好,只用了 11 秒。
【解决方案2】:

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

#!/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

您必须位于要根据日期复制的文件所在的工作目录中。

【讨论】:

  • 你用的是什么操作系统?
【解决方案3】:

我似乎找到了适合我的答案,这在 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”。这样可以吗?
  • 你在使用 OSX 吗?对我来说,它没有这样做:/
  • @ShannonHochkins:你最后用了什么?
  • Matt Danihy 在此non answer 中发表评论,answer 效果很好,但文件名带有空格时存在问题。没有空格它排序就好了,有空格它将所有内容转储到目标文件夹的根目录
  • matt - 在我的 OP 中,我不需要文件路径或文件路径中有任何空格,该逻辑可以为您扩展并且与此 OP 无关
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-07
  • 1970-01-01
  • 2011-04-23
相关资源
最近更新 更多