我有一个包含 500,000 张图像的文件夹,按年份和月份分类在子文件夹中。我想创建一个脚本来执行此操作:
如果文件名才不是匹配任何文件名,names.log
然后删除该文件。
names.log
将包含文件名,例如:
image1.jpg
photo3.jpg
redcar.jpg
balloon2323.jpg
等等......它有大约 10,000 个我想保留的文件名
我的服务器上有 PHP 和 Python,但我不确定什么最适合这个。我之前没有写过任何脚本。有人可以给我一段可以实现这一目标的代码片段并让我知道如何运行它吗?或者也许这可以通过命令来实现?
答案1
这在 Python 中相当简单os.walk
。警告,未经测试的代码。我假设姓名列表每行包含一个姓名,
#!/usr/bin/python2
import os
names_file = open('names.log')
names = set(line.rstrip('\n') for line in names_file.readlines())
names_file.close()
for root, dirs, files in os.walk('/path/to/top/directory'):
for name in files:
path = os.path.join(root, name)
if os.path.isfile(path):
if name not in names:
print path
#os.remove(path) # uncomment this line if you're happy with the set of files to remove
答案2
find -name '*.jpg' -print0 | grep -zZ -vf name.log | xargs -0 COMMAND
替换COMMAND
为ls -l
如果你喜欢的话rm
编辑:所提供的命令将 name.log 视为一组正则表达式。 @terdon 记得 name.log 是文件名列表。
如果文件名使用通常的“正常”字符,这可能就足够了,但如果出现以下情况,可能会出现问题:
- 文件名/正则表达式包含不常见的字符,如
[
、]
等(这种情况下可能会删除某些文件失败,甚至可以删除一些名为 的文件name.log
!)。为了避免这种情况,我们可以使用grep -F
或保护 中的特殊字符name.log
。 - 正则表达式匹配文件名的子字符串(在这种情况下,某些文件不会被删除 -
a.jpg
将匹配所有以“a”结尾的图像,例如camera.jpg
,banana.jpg
)。
对于情况 2,对于前缀情况, -- 我们可以在正则表达式的开头添加“/”。
sed 's!^!/!' name.log > new.log
find -name '*.jpg' -print0 | grep -F -zZ -vf new.log | xargs -0 COMMAND
甚至
find -name '*.jpg' -print0 | grep -zZFvf <(sed 's!^!/!' name.log) | xargs COMMAND
对于情况 2 ,后缀情况不太重要,因为图像文件具有扩展名。为了正确解决这种情况,我们需要说“文件名后面没有任何内容”:我们需要正则表达式,并且.
[
]
文件名中的特殊字符( example )需要受到保护。
sed -re 's!([].[])!\\\1!g; s!.*!/&$!' name.log > new.log
find -name '*.jpg' -print0 | grep -zZ -vf new.log | xargs -0 COMMAND
答案3
这是真的简单的w/ pax
。它有一个替换选项的概念-s
,可以在写入文件名时更改文件名。您-s
也可以指定多个替代参数。而且,这里最相关的是,所选成员仅应用-s
成功进行一次匹配所需的替换参数,但任何导致空文件名的替换都会导致匹配文件不被选择。
展示:
mkdir test; cd test
touch match nomatch
pax -ws '|^.*/match$|&|' -s '|.*||' ./ |
pax -v
上面的./test
代码创建并更改了一个目录,创建了两个文件,然后-w
将一个tar
存档写入一个管道,pax
其中第二个文件详细列出了该管道的内容pax
-v
。以上打印:
-rw-r--r-- 1 mikeserv mikeserv 0 Feb 22 11:40 ./
...因为./match
匹配前最终替换,替换任何文件名中的所有字符。
并且pax
您实际上不必将文件的内容复制到其存档 - 您可以使用-rwl
创建硬链接的复制操作。
所以如果你的文件被命名paxscript
并且看起来像......
cd -- "$1"
pax -rwvl \
-s '|^.*/image1\.jpg$|&|' \
-s '|^.*/photo3\.jpg$|&|' \
-s '|^.*/redcar\.jpg$|&|' \
-s '|^.*/balloon2323\.jpg$|&|' \
-s '|.*||' ./ ../"${1##*/}.mirror"
cd - >/dev/null
...然后你就这样运行...
. ./paxscript "$targetdir"
"$targetdir"
它将在其父目录中创建一个镜像,仅包含指向您希望匹配的文件名的硬链接。然后,您可以在执行之前验证结果是否符合您的喜好,rm -rf "$targetdir"
并删除所有您不需要的路径名。
答案4
如果您满意,bash
我想提供以下算法(可以在任何脚本语言上实现):
- 构建当前文件列表:
find /path_to_folder -name "*.jpg" -fprint files.tmp
- 排序
files.tmp
并name.log
比较其依据comm -23 files.tmp name.log
files.tmp
传递torm
命令特有的文件列表
请注意文件路径 - 两者中的文件路径files.tmp
可能name.log
相同(完整或相对于一个文件夹)。在这种情况下,文件名分隔符name.log
将为newline
.