Bash脚本读取EXIF,重命名JPG文件并检查输出文件退出 - >创建newfile-01.jpg

时间:2014-11-16 23:33:36

标签: bash

更新:2014年12月10日 - 固定!! 脚本在文件夹+ all_subfolders中查找所有jpg,jpeg,JPG文件,并设置文件修改时间戳= exif日期/时间。 +它也将图像的大小写入文件名。 (通过查看filname来了解图像的像素大小)

原件: 我一直在编写一个脚本,拿起信息和信息的痕迹来使用。 我设法制作了一个剧本。在文件夹中运行它,它将获取当前和子文件夹中的所有.jpg .JPG并将它们重命名为filename。即: 2014_10_19_12_24_05_DSC-RX100_F5.6_f37.1mm_1T250s_ISO320.jpg

问题
照片可能是以快速快门速度同时拍摄的。例如:
2014_10_19_12_24_05_DSC-RX100_F5.6_f37.1mm_1T250s_ISO320.jpg(原文件名P00002727.jpg)
2014_10_19_12_24_05_DSC-RX100_F5.6_f37.1mm_1T250s_ISO320.jpg(原文件名P00002728.jpg)

第一个文件写得好,然后第二个文件覆盖第一个文件: - /

如果有人可以提供帮助
我希望脚本有一个" if文件存在,新文件名应该重命名为增加数字01,02,03..etc .."在文件名的末尾。像这样:

2014_10_19_12_24_05_DSC-RX100_F5.6_f37.1mm_1T250s_ISO320.jpg
2014_10_19_12_24_05_DSC-RX100_F5.6_f37.1mm_1T250s_ISO320_01.jpg(已添加_01,02,03等..)

我使用的脚本:

#!/bin/bash

#extensions="jpg,jpeg,png,gif,psd,bmp,crw,thm,tif,tiff"
fileTypes="jpg,jpeg,JPG"

#make regex to find files with extensions in $fileTypes
fileTypes=".*\.\(${fileTypes//,/\|}\)"

# loop through all the image files
find . -iregex "$fileTypes" -print0 | sort | while read -d "" s
#find . -iregex '.*\.\(jpg\|JPG\|jpeg\)$' -print0 | while read -d $'\0' s
#find . -name "*.jpg" -print0 | while read -d $'\0' s

#find . -type f -iname "*.jpg" -print0 | while read -r -d $'\0' s ;

#find . -iname "*.jpg" -print0 | while read -d $'\0' s ;


#for s in $(find . -iname *.jpg -print0 | while read -d $'\0' s);
do
echo "------------  Start --------------------------"

echo ""
let counting=counting+1
echo "Number of files: $counting"

## skip files that already contain _XX increment
#ffn="${s##*/}"                                        # full filename w/o path
#fn="${ffn%.*}"                                        # split filename from ext
#if [ "${fn: -3:1}" = '_' ]; then                      # have we added '_XX' before ?
#    continue                                          # skip to next file 's'
#fi

header=`jhead "$s"`                                     # header holds jhead info about
justFileName=`basename "$s" | sed 's/\(.*\)\..*/\1/'`
dateTime=`echo "$header" | grep "Date/Time"`            # dateTime fetches Date Time line
fileDate=`echo "$header" | grep "File date"`

path="`pwd "$s"/ `"
file="`echo "$s" | sed 's/^.//'`"    # . = 1. first chars is removed in front
pathwithfile="`echo $path$file`"     # does not lookup directory with spaces
pathwithspaces="$PWD$file"           # does lookup direcotyr with spaces!
justPath=`dirname "$pathwithfile"`

exifdateTime=`echo $dateTime | awk -F ":" '{print $2"_"$3"_"$4"_"$5"_"$6}' | awk -F " " '{print $1"_"$2}'`

dateFileTime=`echo $fileDate | awk -F "_" '{print $1"_"$2"_"$3"_"$4"_"$5"_"$6}' | awk -F ":" '{print $2"_"$3"_"$4"_"$5"_"$6}'`
#| cut -c 16-34`
#datefileName=`echo $justFileName | cut -c 1-19`



FULLPATH="$pathwithspaces"
# remove all the prefix until "/" character
FILENAME=${FULLPATH##*/}
# remove all the prefix unitl "." character
FILEEXTENSION=${FILENAME##*.}
# remove a suffix, in our cas, the filename, this will return the name of the directory that contains this file
BASEDIRECTORY=${FULLPATH%$FILENAME}

#echo "FULLPATH = $FULLPATH"
#echo "FILENAME = $FILENAME"
#echo "FILEEXTENTION = $FILEEXTENSION"
#echo "BASEDIRECTORY = $BASEDIRECTORY"


#echo "dateTime: $dateTime"
#echo "exifdateTime: $exifdateTime"
#echo "fileDate: $fileDate"
#echo "dateFileTime: $dateFileTime"
#echo "pathwithfile: $pathwithfile"
#echo "pathwithspaces: $pathwithspaces"



stripedfilename=`echo $FILENAME | cut -c 1-19`

if [ $exifdateTime = $stripedfilename ]; then
echo "File \"$s\" already exists with exifdateTime: $stripedfilename"
echo "..skip to next file"
echo "--- End ---"

continue
        stripedfiledate=$fileDate | cut -c 1-19
        if [ $stripedfiledate = $stripedfilename ] ; then
        echo "File \"$s\" already exists with fileDate: $stripedfiledate"
        echo "..skip to next file"
        echo "--- End ---"
        continue
        fi
fi

#echo "break"
#break




#if [ -z "$dateTime" ] || [[ $dateTime == "Date/Time    : 0000:00:00 00:00:00" ]];
if [ "$dateTime" = "Date/Time    : 0000:00:00 00:00:00" ]
then

        if [ -z "$fileDate" ]
        then
                # If Date/Time=0000:00:00 check File date
                dateTime=`echo "$header" | grep "File date"`
#               echo "Date/Time(ny): $fileDate"

                # Set timestamp from exif
#               echo "File date(ny): $exifdateTime"
                jheadrun=`jhead -autorot -exonly -ft -norot "$FULLPATH"`

        else
                # Set exif = modified timestamp.
                jheadrun=`jhead -dsft "$FULLPATH"`

#echo "..fixed Date/Time:   0000..."
#echo "break 1"
#break

        fi



# Set new dateTime
header=`jhead "$s"`                                     # header holds jhead info about
dateTime=`echo "$header" | grep "Date/Time"`
#justFileName=`basename "$s" | sed 's/\(.*\)\..*/\1/'`
exifdateTime=`echo $dateTime |  awk -F ":" '{print $2"_"$3"_"$4"_"$5"_"$6}' | awk -F " " '{print $1"_"$2}'`


fi


# if -z variable is zero
# if -n variable is none-zero

if [ "$fileDate" = "" ] ;
then
#echo "break 2"
#break

#echo "breake 2 - True"
        # Set exif = modified timestamp.
        jheadrun=`jhead -dsft "$FULLPATH"`
#       echo "jhead -dsft $jheadrun"
else
#echo "break 2 - False"
        # Set Date/Time from File-modification timestamp
#        echo "Date/Time(ny): $exifdateTime"
        jheadrun=`jhead -autorot -exonly -ft -norot "$FULLPATH"`
#       echo "`jhead -autorot -exonly -ft -norot \"$FULLPATH\"`"

fi




#
# check file do have Modification date
# if yes
# set Date/Time = modification date
#
if [ "$dateTime" = "" ] ;
then
#echo "break 3"
#break

echo "breake 3 - True"
        # Set exif = modified timestamp.
        jheadrun=`jhead -mkexif -dsft "$FULLPATH"`
        echo "jhead -mkexif -dsft $jheadrun"
fi



if [ "$exifdateTime" != "$dateFileTime" ]
then

#echo "break 4"
#break



# echo $s
 x=`jhead "$s" | \
 awk 'BEGIN { cmt=""; }
#/File name/      { n=$4; gsub(".jpg","",n);}
/Camera model/   { c=$4$5$6;}
/Exposure time:/ { e=$3;
                   if (e==int(e)) e=int(e);
                   if (e<1) {e=int(0.5+1/e); e="1T" e "s";} else { e=e "s"; }
                 }
/ISO equiv./     { iso="ISO" $4; }
/Focal length/   { f="f" $4; }
/Date.Time /     { d=$3 "_" $4; gsub(":","_",d); }
/Resolution /    { size=$3$4$5$6; }
/Aperture /      { ap=$3; gsub("f/","F",ap); }
/Comment  /      { cmt=$3 "_"; }
END { print d "_" c "_" size "_" ap "_" f "_" e "_" iso ".jpg"; }'`


commentcheck=`echo "$header" | grep Comment | awk -F ":" '{print $2}'`


if [ "$commentcheck" != " Original_filename" ]
then
#echo "break 5"

comment="Original_filename: $FILENAME"
commentcommand="jhead -cl \"$comment\""


echo "..no comment in file, adding; $comment  <-- adding comment"

#echo "commment: $comment"
#echo "commentcheck: $commentcheck      <-- if this is blank file does not have comment"
#echo "commentcommand: $commentcommand"

addcomment="$commentcommand \"$pathwithfile\""
#addcomment=("jhead -cl $comment $pathwithfile")

#echo "addcomment: $addcomment"

eval $addcomment  # Run variable in terminal

else
continue

fi

#echo "break 6"
#break

########



PADDING=2
NEWFILE="$BASEDIRECTORY$x"

if [[ -f $NEWFILE ]]; then
    BASE=`echo $x | sed "s/\.jpg/_/"`
#echo $BASE
    LAST=`ls -1 "$BASEDIRECTORY" | grep $BASE | sort -r | head -1`
#echo $LAST
    LASTNUM=`echo ${LAST:${#x}-3:$PADDING} | sed "s/^0*//"`
#echo $LASTNUM
    let LASTNUM=LASTNUM+1
#echo $LASTNUM
    FINAL=$BASE$(printf "%0"$PADDING"d.jpg" ${LASTNUM})
#echo $FINAL
    NEWFILE=$BASEDIRECTORY$FINAL
fi


mv "$s" "$NEWFILE"
#NODUPLICATE="jhead -n%f-%02i \"$pathwithspaces\" \"$NEWFILE\""
#echo "mv \"$pathwithspaces\" \"$NEWFILE\""

echo "New filename: $NEWFILE"



#echo "---"
#header=`jhead "$NEWFILE"`
#echo "$header"
#echo "---"

#echo "break 6"
#break

#echo "$s"

fi # END OF if [ "$exifdateTime" != "$datefileName" ]

unset x
#echo "$x"

#echo "Sleeping 1/100 of a second"
sleep 0.01
#sleep 0.05
#sleep 3

echo "End..------------------------------------------------"
echo ""
done

3 个答案:

答案 0 :(得分:1)

在:

mv "$s" "$NEWFILE"

使用类似的东西:

PADDING=2
echo "New filename: "
NEWFILE="$BASEDIRECTORY$x"
if [[ -f $NEWFILE ]]; then
    BASE=`echo $x | sed "s/\.jpg/_/"`    
    LAST=`ls -1 $BASEDIRECTORY | grep $BASE | sort -r | head -1`
    LASTNUM=`echo ${LAST:${#x}-3:$PADDING} | sed "s/^0*//"` 
    let LASTNUM=LASTNUM+1
    FINAL=$BASE$(printf "%0"$PADDING"d.jpg" ${LASTNUM})
    NEWFILE=$BASEDIRECTORY$FINAL
fi

您当然可以使用脚本的counter来处理序列号,但此版本在重新运行时不会重新计算(我认为更安全)。

答案 1 :(得分:1)

你创建NEWFILE="$BASEDIRECTORY$x"的地方你需要这样的东西:

#!/bin/bash

echo "New filename: "
NEWFILE="$BASEDIRECTORY$x"
ntmp="$NEWFILE"
while [ -e "$ntmp" ]; do                                  # while the file "$ntmp" exist...
    ffn="${ntmp##*/}"                                     # full filename w/o path
    fn="${ffn%.*}"                                        # split filename from ext
    ext="${ntmp#*${fn}.}"                                 # get the ext
    if [ "${fn: -3:1}" = '_' ]; then                      # does filename have '_XX' ?
        if [[ "${fn: -2}" =~ [^0-9] ]]; then              # if 'XX' isn't 2 digits [0-9]
            ntmp="${fn}_01.${ext}"                        # it's not ours, just add _01, and
            continue                                      # continue
        fi
        count=$((${fn: -2}+1))                            # if 2-digits, get the XX and add +1
        if [ "$count" -lt 10 ]; then
            ntmp="${fn:0:$((${#fn}-3))}_0${count}.${ext}" # if 1-digit, replace w/ '_0${count}'
        else
            ntmp="${fn:0:$((${#fn}-3))}_${count}.${ext}"  # if 2-digit, replace w/ '_${count}'
        fi
    else                                                  # if first dup, just add '_01'
        ntmp="${fn}_01.${ext}"
    fi
done

mv "$s" "$ntmp"

重复文件名:

$ ls -1 20*
2014_10_19_12_24_05_DSC-RX100_F5.6_f37.1mm_1T250s_ISO320.jpg
2014_10_19_12_24_05_DSC-RX100_F5.6_f37.1mm_1T250s_ISO320_01.jpg
2014_10_19_12_24_05_DSC-RX100_F5.6_f37.1mm_1T250s_ISO320_02.jpg

注意:使用反向字符串索引(例如${fn: -2})时,必须在{{1}之前加一个空格}符号(或将索引放在括号中(例如-)。

${fn:(-2)}中工作时尝试利用bash提供的工具并远离使用外部应用程序和管道(每个应用程序和管道产生一个单独的进程和子shell),例如(bash,{ {1}},grep和&#39; |&#39;)。实际上,只要字符串/行解析利用其自己的参数扩展/子字符串提取/子字符串替换字符串索引,bash就无法做任何事情。从脚本中调用外部应用程序没有任何问题,只是不要用它们来代替已经为你做的bash。

由于您使用的是awkcut,因此您可能会对此Iphone Image Rename w/jhead脚本中的一些想法感兴趣。这需要稍微不同的方法来解析bash图像输出,并且有一些方便的功能可以使用。


根据我们在评论中的讨论,如果您通过脚本提供所有文件名(包括先前以_XX增量递增的文件名),您将希望再次跳过处理这些文件。

要省略已从主循环递增的文件,您需要在jhead循环的开头添加以下内容:

jhead

行。为了深入了解这一点,并帮助我们更有效地进行沟通,我创建了一个测试用例脚本。我遇到的问题是我没有你的相机或文件中的文件知道我正在处理的是什么。这使我试图尽可能广泛地处理所有情况(无论如何都应该这样做)。但以下内容将允许针对输出文件名测试输入文件。我已经保留了原始脚本内容(在注释中)以显示测试所在的上下文,并且我已经评论并替换了测试用例所需的部分。以下是经过验证的脚本和测试用例:

find | while

现有文件:

# loop through all the image files
find . -iregex "$fileTypes" -print0 | while read -d "" s
do

## skip files that already contain _XX increment
ffn="${s##*/}"                                   # full filename w/o path
fn="${ffn%.*}"                                   # split filename from ext
if [ "${fn: -3:1}" = '_' ]; then                 # does the file have '_XX' suffix
    if [[ "${fn: -2}" =~ [0-9][0-9] ]]; then     # if 2 digits, assume prior increment
        continue                                 # skip to next file 's'
    fi
fi

文件重命名:

#!/bin/bash

# fileTypes="jpg,jpeg"

#make regex to find files with extensions in $fileTypes
# fileTypes=".*\.\(${fileTypes//,/\|}\)"

# loop through all the image files
# find . -iregex "$fileTypes" -print0 | while read -d "" s
for s in "$@"
do

## skip files that already contain _XX increment
    ffn="${s##*/}"                                   # full filename w/o path
    fn="${ffn%.*}"                                   # split filename from ext
    if [ "${fn: -3:1}" = '_' ]; then                 # does the file have '_XX' suffix
        if [[ "${fn: -2}" =~ [0-9][0-9] ]]; then     # if 2 digits, assume prior increment
            continue                                 # skip to next file 's'
            echo "XX is digits, skipping"
        fi
    fi

    ## rest of your script here...

    #NEWFILE="$BASEDIRECTORY$x"
    NEWFILE="$s"
    ntmp="$NEWFILE"
    while [ -e "$ntmp" ]; do                                  # while the file "$ntmp" exist...
        ffn="${ntmp##*/}"                                     # full filename w/o path
        fn="${ffn%.*}"                                        # split filename from ext
        ext="${ntmp#*${fn}.}"                                 # get the ext
        if [ "${fn: -3:1}" = '_' ]; then                      # does filename have '_XX' ?
            if [[ "${fn: -2}" =~ [^0-9] ]]; then              # if 'XX' isn't 2 digits [0-9]
                ntmp="${fn}_01.${ext}"                        # it's not ours, just add _01, and
                continue                                      # continue
            fi
            count=$((${fn: -2}+1))                            # if 2-digits, get the XX and add +1
            if [ "$count" -lt 10 ]; then
                ntmp="${fn:0:$((${#fn}-3))}_0${count}.${ext}" # if 1-digit, replace w/ '_0${count}'
            else
                ntmp="${fn:0:$((${#fn}-3))}_${count}.${ext}"  # if 2-digit, replace w/ '_${count}'
            fi
        else                                                  # if first dup, just add '_01'
            ntmp="${fn}_01.${ext}"
        fi
    done

# mv "$s" "$ntmp"

if [ "$s" = "$ntmp" ]; then
    printf "\n  no change   : %s\n\n" "$s"
else
    printf "\n  file exists : %s\n  new fname   : %s\n\n" "$s" "$ntmp"
fi

done

exit 0

答案 2 :(得分:0)

man mv(1)包含您需要的所有信息。这是使用数字备份选项

的示例
% touch aaa1 aaa2 aaa3 aaa4
% for fn in {1..4} ; do mv --backup=t aaa$fn bbb1 ; done
% ls bbb*
bbb1  bbb1.~1~  bbb1.~2~  bbb1.~3~
% 

附录

使用mv --backup=t ...有一个小缺陷,它使用上一个移动文件的普通名称,在我上面的示例中aaa1 -> bbb1.~1~aaa2 -> bbb1.~2~aaa3 -> bbb1.~3~和(但) aaa4 -> bbb1

这与mv POV完全合乎逻辑,可能不太适合OP。

下面的脚本将未编号的文件移动到其逻辑位置 (适用于需要在当天结束时重命名的任意数量的文件)

% for x in $(sed s/....\$// <(printf %s\\n *.\~?\~) | uniq) ; do
>   if [ -f $x ] ; then
>     last=$(ls ${x}*~ |tail -1 | sed -r 's/(.*)~([0-9]+)~$/\2/')
>     mv $x $x.'~'$(($last+1))'~'
    fi
> done
% ls bbb1*
bbb1.~1~  bbb1.~2~  bbb1.~3~  bbb1.~4~
% 

以非常短的顺序拍摄的照片现在都编号为1到N