强大的 Exiv2 助 Synology 照片整理一臂之力
3 年前
- 昨天整理照片的时候用到一个新工具 Exiv2 ,配合脚本: 按照片的拍摄时间重命名 的功能。为什么要改名?以下文件名大家感受下就好:
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