强大的 Exiv2 助 Synology 照片整理一臂之力

强大的 Exiv2 助 Synology 照片整理一臂之力

3 年前
1360654147987.jpeg
-1998496702.jpeg
2008-12-27-0.bmp
2013-07-13 21.55.00-1.jpg
2013_12_24_18_22_27.jpg
20140217_195036_Android.jpg
2014_07_13_23_34_15.jpg
2015-03-22 155333(1).jpg
2017-11-24 091519.jpg
20180328210442441.jpg
2Q1A3602.JPG
5D3_6064.jpg
6734108497056027763.JPG
DSC_1718.JPG
DSCF1657.jpg
FC047988-8BBA-4DA4-97FE-CAC7E709DC87-8452-00000D1BB3131E23_tmp_1.jpg
hdImg_c3511400.jpg
IMG_2008.HEIC
IMG_20120520_131912_compress.jpg
IMG_20141018_154658_1_1538025858307.jpg
IMG_20141109_132144_1.jpg
IMG20141223214713.jpg
IMG_20150322_160154_BURST2.jpg
IMG20160704222659.jpg
IMG_20170422_005407_HDR.jpg
IMG_20171006_131836_HHT.jpg
IMG_2611.PNG
IMG_4775_3.JPG
IMG_9130.CR2
microMsg_1567686722211.jpeg
mmexport1573908606434.jpg
moodSyncWeChat.jpeg
MYXJ_20160318142113_fast.jpg
navi_photo_18666952619.jpg
PA020136.JPG
PANO_20141112_173323.jpg
SAM_0297.jpg
Screenshot_2012-03-28-21-11-41.png
VID_20140419_131717.jpg
wx_camera_1572670076056.jpg
XiaoMi_IMG_20140529_203548.jpg
ZMS_7688.jpg
图像001.PNG
照片 618.JPG
手动用 exiv2 给图片重命名的示例


exiv2 -v -r'%Y%m%d-%H%M%S' -f IMG_13331.jpg
# File 1/1: IMG_13331.jpg
# Renaming file to ./20180916-133120.jpg
  • 支持批量操作,且支持名字重复加后缀的功能。
exiv2 -v -r'%Y%m%d-%H%M%S' -F  *.{jpg,jpeg,png,cr2,heic}
  • 手机拍摄的照片都包含了拍摄时间。截图、处理后的照片、古老的照片,没有拍摄时间的用 Exiv2 追加上:
exiv2 -pa IMG_0516.jpg
exiv2 -v -M"set Exif.Photo.DateTimeOriginal 2009:10:24 22:33:33" IMG_0516.jpg
exiv2 -v -M"set Exif.Photo.UserComment From:~higkoo/IMG_0516.jpg" IMG_0516.jpg
  • 爽快,把文件统统来个改名,一键搞定:
find /volume1/photo/MobileDevice ! -path "*@eaDir*" -type f -exec exiv2 -v -r'%Y%m%d-%H%M%S' -f {} \;
  • 然后再把重命名成功的文件按年份归档起来,补个简单的 脚本
#!/bin/bash
# Copy photos to the format path
sDir="${1:-/volume1/photo/MobileDevice/}"  # 需要扫描的源目录
dDir="${2:-/volume1/homes/higkoo/XiaoMi/Image/}" # 归档目录
mDepth="${3:-2}" # 默认只进2层目录
readonly debug=false  # 调试开关
readonly iLog="${dDir}FileList.csv"  # 扫描日志
readonly eLog="${dDir}Error.log"
declare -r mCode=([0]="Noneed" [1]="Created" [2]="Exsit" [3]="Overwrite" [4]="Failed" [5]="Error" [6]="Unknow")  # 错误码定义
declare -A dPathInfo=([Y]=2000 [m]=11 [d]=22 [H]=11 [M]=22 [S]=33 [p]="/dev/null")  # 解析文件名信息
declare -a dDirList=() # 减少文件操作,用临时内存操作替换
rCode=6 # 错误码
function AddLog(){
        echo "${rCode:-6},$(date +%F\ %T),${sPath},${dPathInfo[p]}" >>"${iLog}" 2>>"${eLog}" && return 0 || return 1
function ParsePath(){
        dPathInfo=() # 清空
        sfName="${sPath##*/}"  # 文件名(带后缀)
        spName="${sfName%.*}"  # 文件名(无后缀)
        stStr="${spName#*-}"   # 时分秒串
        sExt="${sfName##*.}"   # 后缀名
        [ "${sfName:8:1}" = "-" -a ${#spName} -eq 15 ] || return 1  # 用文件名判断合法性
        read dPathInfo[Y] dPathInfo[m] dPathInfo[d] dPathInfo[H] dPathInfo[M] dPathInfo[S] < <(date -d "${spName%-*} ${stStr:0:2}:${stStr:2:2}:${stStr:4:2}" +"%Y %m %d %H %M %S") 2>>"${eLog}" || rCode=5
        dPathInfo[p]="${dDir}${dPathInfo[Y]}/${spName}.${sExt,,}" && return 0  # 目标全路径 、文件后缀统一转成小写
        return 1
touch "${iLog}" "${eLog}"
find "${sDir}" -maxdepth "${mDepth}" ! -path "*@eaDir*" -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.heic" -o -iname "*.png" -o -iname "*.cr2" | while read sPath; do
    unset sfName spName stStr sExt  # 避免循环过程遇到错误时信息污染
        $debug && sPath='/volume1/photo/MobileDevice/iPhone/2018-09-16 105911.jpg'  # 调试用
        grep -q "${sPath}" "${iLog}" 2>>"${eLog}"
        if [[ $? -eq 0 ]]; then
                echo -ne '.'  # 已扫描过,跳过
                continue
        ParsePath
        if [[ $? -ne 0 ]]; then
                rCode=0 && AddLog
                echo -ne '_'  # 非格式化的文件名,跳过
                $debug && echo "${sPath}"
                continue
        $debug && declare -p dPathInfo dDirList
        if [[ ! ${dDirList[*]} =~ (^|[[:space:]])"${dPathInfo[Y]}"($|[[:space:]]) ]]; then  # 判断目录是否已经创建过
                mkdir -pv "${dDir}${dPathInfo[Y]}/" && dDirList+=("${dPathInfo[Y]}")
        if [[ ! -f "${dPathInfo[p]}" ]]; then
                cp -v "${sPath}" "${dPathInfo[p]}" 2>>"${eLog}" && rCode=1 || rCode=4
                if [[ `stat --printf=%s "${dPathInfo[p]}"` -lt `stat --printf=%s "${sPath}"` ]]; then  
                         cp -v "${sPath}" "${dPathInfo[p]}" 2>>"${eLog}" && rCode=3 || rCode=4