记录一下我删除相机 SD 卡当中废片的工作流
采用 python 脚本实现(python3)。
相关说明:
- Linux/mac 电脑,如果你是 windows 电脑那么脚本需要稍加改造
- 相机当中开启了拍摄的照片按照日期文件夹存储,这种功能,否则可能一个文件夹下的照片数量特别多,你需要维护一个特别长的 keep.txt
- keep.txt 当中的文件名可以是完整文件名,也可以不包含扩展名。
a. 如果是完整文件名,那脚本就根据完整文件名匹配去保留/删除照片
b. 如果只包含文件名 stem,没有扩展名,将匹配所有包含 stem 的文件名,进而执行保留/删除逻辑。比如 keep.txt 当中有这样一行 HDK09432 ,那这些文件 HDK09432.JPG, HDK09432.ARW, HDK09432.HEIF 都会被保留,这尤其适用于你开启了 JPEG/RAW 拍摄,那么一张图片会有两个文件 - 支持模拟运行,只需要在最后添加
--dry-run - 索尼 JPEG 照片扩展名是
JPG,RAW 拍摄文件扩展名是ARW,HEIF 拍摄扩展名是HIF(Apple 的 HEIF 照片扩展名是HEIC)
cd /run/media/dk/708D-3AF3/DCIM/15660613/
ls
HDK09429.JPG HDK09432.JPG HDK09435.JPG HDK09438.JPG HDK09441.JPG HDK09444.JPG HDK09447.JPG
HDK09430.JPG HDK09433.JPG HDK09436.JPG HDK09439.JPG HDK09442.JPG HDK09445.JPG HDK09448.JPG
HDK09431.JPG HDK09434.JPG HDK09437.JPG HDK09440.JPG HDK09443.JPG HDK09446.JPG HDK09449.JPG
$python3 ~/Downloads/myapps/delete_unwanted_images.py ./keep.txt --dry-run
========== 执行模式 ==========
DRY RUN
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG
========== 保留文件 ==========
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
========== 统计 ==========
模式: DRY-RUN
计划删除文件数: 15
保留文件数: 6
========== 删除列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG
========== 保留列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
$python3 ~/Downloads/myapps/delete_unwanted_images.py ./keep.txt
========== 执行模式 ==========
REAL DELETE
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG
========== 保留文件 ==========
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
========== 统计 ==========
模式: DELETE
计划删除文件数: 15
保留文件数: 6
实际删除成功数: 15
========== 删除列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG
========== 保留列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
ls
HDK09432.JPG HDK09438.JPG HDK09440.JPG HDK09441.JPG HDK09442.JPG HDK09449.JPG keep.txt
脚本内容:
$cat ~/Downloads/myapps/delete_unwanted_images.py
#!/usr/bin/env python3
import os
import sys
IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".arw", ".heic", ".heif", ".hif", ".dng"}
def load_keep_list(path):
exact_keep = set()
stem_keep = set()
with open(path, "r", encoding="utf-8") as f:
for line in f:
raw = line.strip()
if not raw:
continue
stem, ext = os.path.splitext(raw)
if ext: # 有扩展名 → 精确匹配
exact_keep.add(raw)
else: # 无扩展名 → stem 匹配
stem_keep.add(stem)
return exact_keep, stem_keep
def is_image_file(filename):
ext = os.path.splitext(filename)[1].lower()
return ext in IMAGE_EXTS
def main():
if len(sys.argv) < 2:
print("Usage: python3 delete_unwanted_images.py ./keep.txt [--dry-run]")
sys.exit(1)
keep_path = sys.argv[1]
dry_run = "--dry-run" in sys.argv[2:]
root_dir = os.getcwd()
if not os.path.exists(keep_path):
print(f"[ERROR] keep.txt not found: {keep_path}")
sys.exit(1)
exact_keep, stem_keep = load_keep_list(keep_path)
to_delete = []
to_keep = []
# 扫描文件
for root, dirs, files in os.walk(root_dir):
for file in files:
if not is_image_file(file):
continue
full_path = os.path.join(root, file)
stem = os.path.splitext(file)[0]
if (file in exact_keep) or (stem in stem_keep):
to_keep.append(full_path)
else:
to_delete.append(full_path)
deleted_count = 0
print("\n========== 执行模式 ==========")
print("DRY RUN" if dry_run else "REAL DELETE")
# 删除逻辑
for path in to_delete:
if dry_run:
print(f"[DRY-RUN] 将删除: {path}")
else:
try:
os.remove(path)
print(f"已删除: {path}")
deleted_count += 1
except Exception as e:
print(f"[ERROR] 删除失败 {path}: {e}")
# 保留输出
print("\n========== 保留文件 ==========")
for path in to_keep:
print(f"保留: {path}")
# 统计
print("\n========== 统计 ==========")
print(f"模式: {'DRY-RUN' if dry_run else 'DELETE'}")
print(f"计划删除文件数: {len(to_delete)}")
print(f"保留文件数: {len(to_keep)}")
if not dry_run:
print(f"实际删除成功数: {deleted_count}")
# 列表输出
print("\n========== 删除列表 ==========")
for path in to_delete:
print(path)
print("\n========== 保留列表 ==========")
for path in to_keep:
print(path)
if __name__ == "__main__":
main()
keep.txt 内容示例
$cat keep.txt
HDK09432
HDK09438
HDK09440
HDK09441
HDK09442
HDK09449