按日期将文件排序到子文件夹中 - bash
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27475057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sort files into sub folders by date - bash
提问by Shannon Hochkins
Basically my HDD crashed, I was able to recover all the files, but, all the files have retained their meta & some have retained their names, I have 274000 images, which I need to more or less, sort into folders by date.
基本上我的硬盘崩溃了,我能够恢复所有文件,但是,所有文件都保留了它们的元数据,有些文件保留了它们的名称,我有 274000 张图像,我需要或多或少地按日期分类到文件夹中。
So let's say it starts with the first files, it would get the date from the file, create a sub folder, and until the date changes, keep moving that file into the created folder, once the date changes, it would create a new folder and keep doing the same thing.
所以假设它从第一个文件开始,它会从文件中获取日期,创建一个子文件夹,直到日期发生变化,继续将该文件移动到创建的文件夹中,一旦日期发生变化,它将创建一个新文件夹并继续做同样的事情。
I'm sure this is possible, I really didn't want to have to do this manually as it would take weeks...
我确定这是可能的,我真的不想手动执行此操作,因为这需要数周时间......
Lets say I have a target folder /target/
假设我有一个目标文件夹 /target/
Target contains, 274000 files, in no sub folders at all.
Target 包含 274000 个文件,根本没有子文件夹。
The folders structure should be /target/YY/DD_MM/filenames
文件夹结构应该是/target/YY/DD_MM/filenames
I would like to create a bash script for this, but I'm not really sure where to proceed from here.
我想为此创建一个 bash 脚本,但我不确定从哪里开始。
I've found this:
我发现了这个:
#!/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
Would creating a folder without checking if it exists delete files inside that folder?
创建一个文件夹而不检查它是否存在会删除该文件夹中的文件吗?
I'm also not quite sure what adding an asterix to the dir pathname would do?
我也不太确定在目录路径名中添加星号会做什么?
I'm not quite familiar with bash, but I'd love to get this working if someone could please explain to me a little more what's going on?
我对 bash 不太熟悉,但如果有人可以向我解释一下发生了什么,我很乐意让它工作?
Thankyou!
谢谢!
回答by Shannon Hochkins
I seemed to have found an answer that suited me, this worked on OSX just fine on three files, before I run it on the massive folder, can you guys just check that this isn't going to fail somewhere?
我似乎找到了一个适合我的答案,这在 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
回答by grizmin
I've had issues with the performance of the other solutions since my filesystem is remotely mounted and access times are big.
由于我的文件系统是远程安装的并且访问时间很长,因此我遇到了其他解决方案的性能问题。
I've worked on some improved solutions in bash and python:
我在 bash 和 python 中研究了一些改进的解决方案:
Bash version:
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 #
蟒蛇版本:
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 #
Both scripts are tested upon 319 mkv files modified in the last 3 days.
这两个脚本都在过去 3 天内修改的 319 个 mkv 文件上进行了测试。
回答by repzero
I worked on a little script and tested it.Hope this helps.
我编写了一个小脚本并对其进行了测试。希望这会有所帮助。
#!/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 }'|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
You must be in your working directory where your files to be copied according to date are present.
您必须位于工作目录中,要根据日期复制的文件所在的位置。