#!/bin/bash # http://nicofo.tuxfamily.org # 21/10/06 : v1 : version de base - photos uniquement (renamePhotos) # 10/12/06 : v2 : tout type de fichier (renomme) # 17/03/07 : v3 : ajout option --comment # 30/04/07 : v4 : ajout option --addComment|-a # 07/09/07 : v5 : ajout addComment sur une seule photo # 16/10/08 : v6 : ajout option -i # 06/09/09 : v7 : diaporama : ajout options --dHP / --dCANON # 10/10/09 : v8 : fusion avec le script 'datePhotos' (-d|--date), ajout --undo et --diapo # nouvelle version addComment (-a), ajout supComment (-s) # 03/01/10 : v8.1 : ajout de "TMPTMP" pour toutes les photos # 28/02/10 : v8.2 : bug avec variable 'index' => 'index_com' # 1) Obtenir les metadata d'une photo avec ImageMagick : # identify -format %[EXIF:*] photo.jpg # Toutes # identify -format %[EXIF:DateTimeOriginal] photo.jpg # Juste la date de prise de vue # # 2) Obtenir les metadata d'une photo avec exiftool : # exiftool -"DateTimeOriginal" -s -s -s photo.jpg usage() { echo -e "Usage : $(basename $0) [-d|--date|-c|--chronologique] [-i|--index ]\n\t\t[-n|--nospace] [--comment] [--diapo] [--dHP|--dCANON]\n\t\t[-y|--noconfirm] [dossier] [nomFichiers]" echo -e " $(basename $0) -d|--date [--check] [--affiche] [--ajoutDate]" echo " $(basename $0) -a|--addComment" echo " $(basename $0) -s|--supComment" echo " $(basename $0) --undo" echo echo -e " \033[1mCritères de renommage :\033[0m" echo -e " \033[34m-d|--date\033[0m (photos) ==> tri en fonction de la date contenue dans les\n\t\t\t\t données EXIF" echo -e " \033[34m-c|--chronologique\033[0m ==> tri chronologique (date du fichier)" echo " par défaut ==> tri alphabétique" echo echo -e " \033[1mOptions générales :\033[0m" echo -e " \033[34m-n|--nospace\033[0m : n'ajoute pas d'espace dans le nom des fichiers" echo -e " \033[34m-i|--index\033[0m : index à partir duquel les fichiers sont numérotés\n\t\t(1 par défaut)" echo -e " \033[34m--comment\033[0m : tient compte d'un comment dans le nom du fichier lors\n\t\tdu renommage (ex. : photo 45 comment.jpg)" echo -e " \033[34m--diapo\033[0m : tient compte d'un diaporama dans le nom du fichier lors\n\t\tdu renommage (ex. : photo 45_4.jpg)" echo -e " \033[34m--dHP / --dCANON\033[0m : tient compte des fichiers diaporama HP / CANON" echo -e " \033[34m-y|--noconfirm\033[0m : ne demande pas de confirmation avant le renommage" echo echo -e " \033[34mdossier\033[0m : renomme les fichiers du dossier" echo " Si non donné, dossier courant par défaut" echo -e " \033[34mnomFichiers\033[0m : 1) avec l'option --date : ne prend en compte que les\n\t\t\t photos du nom '*nomPhotos*.jpg'" echo -e " 2) sans l'option --date : renomme les fichiers donnés.\n\t\t\t ex. : $(basename $0) *.txt" echo " Si non donné, renomme toutes les photos jpg" echo echo -e " \033[1mOptions particulières du tri par date (-d|--date) :\033[0m" echo -e " \033[34m--check\033[0m : ne fait que tester si les photos sont bien triées" echo -e " \033[34m--affiche\033[0m : affiche la date des photos" echo -e " \033[34m--ajoutDate\033[0m : ajoute la date de la photo dans son nom sous la forme\n\t\t\"photo 45 (jj-mm-aaaa).jpg\"" echo echo -e " \033[34m-a|--addComment\033[0m : ajoute un commentaire (option --total pour" echo -e " \033[34m-s|--supComment\033[0m : supprime un commentaire recherche approfondie)" echo -e " \033[34m--undo\033[0m : annule la dernière opération de renommage" exit $1 } FIRST=1 #1 tant que aucun "move" n'a été fait ; 0 sinon move() { [ $FIRST -eq 1 ] && FIRST=0 && rm $UNDO 2>/dev/null mv -i "$1" "$2$3" #-i ==> prévient si overwrite un fichier echo "$1" >> $UNDO echo "$2" >> $UNDO } ############# FONCTION addComment ############# addComment() { [ "$1" = "-s" ] && DELETE=1 && shift #RAPIDE=0 => test sur tous les fichiers ; RAPIDE=1 => test sur les photos à partir de $begin -> $end [ "$1" = "--total" ] && RAPIDE=0 || RAPIDE=1 read -p "Nombre de départ : " begin [ -n "$(echo $begin | sed s/[0-9]*//)" -o -z "$begin" ] && echo "Nombre de départ non valide !" && exit 1 read -p "Nombre de fin : " end [ -n "$(echo $end | sed s/[0-9]*//)" ] && echo "Nombre de fin non valide !" && exit 1 [ -z "$end" ] && end=$begin && echo -e "\033[1ANombre de fin : $end" [ "$begin" -gt "$end" ] && echo "Erreur : mauvaises entrées !" && exit 1 if [ "$DELETE" = 1 ]; then espaceComment="" else espaceComment=" " read -p "Commentaire : " comment [ -z "$comment" ] && echo "Erreur : commentaire vide !" && exit 1 fi echo #supprime les 0 au début du chiffre (sinon "parfois" considéré comme octal : ex. dans ${..[$num]}, for ((n=$begin;n<$(($end+1));++n)) ) begin=$(echo $begin|sed "s/^0*\(.\)/\1/") end=$( echo $end |sed "s/^0*\(.\)/\1/") [ $(ls |grep "\.jpg$" -c) -eq 0 ] && echo "Pas de fichiers jpg trouvé dans le répertoire courant !" && exit 1 [ "$RAPIDE" = 1 ] && nBegin=$(ls *.jpg|grep " 0*$begin\(_[0-9]\+\)\?[ \.]" -n -m1|cut -d':' -f1) #n° du fichier $begin parmi toutes les photos [ -z "$nBegin" ] && nBegin=0 for i in 1 2 ; do n=0 #pour recherche rapide num="" for file in *.jpg; do n=$(($n+1)) [ "$RAPIDE" = 1 ] && [ $n -lt $nBegin ] && continue [ "$RAPIDE" = 1 ] && [ -n "$num" ] && [ $num -gt $end ] && break #1) recherche de la photo sans comment #OU num=$(expr "$file" : ".* \([0-9]\+\)\.jpg|.* \([0-9]\+\)_[0-9]\+\?\.jpg") #renvoie ce qui matche dans () ; vide sinon num=$(expr "$file" : ".* 0*\([0-9]\+\)\\(_[0-9]\+\\)\?\.jpg") #renvoie ce qui matche dans () ; vide sinon if [ -n "$num" ]; then if [ $begin -le $num ] && [ $num -le $end ]; then Tnocomment[$num]=$((${Tnocomment[$num]}+1)) if [ ! "$DELETE" = 1 ]; then nom=${file%.jpg} #retirer l'extension if [ $i -eq 1 ] ; then echo -e "$file\t ==> $nom \033[32m$comment\033[0m.jpg" else move "$file" "$nom $comment.jpg" fi fi fi else #2) sinon : recherche de la photo avec comment num=$(expr "$file" : ".* 0*\([0-9]\+\)\\(_[0-9]\+\\)\? .\+\.jpg") #renvoie ce qui matche dans () ; vide sinon if [ -n "$num" ]; then if [ $begin -le $num ] && [ $num -le $end ]; then Twithcomment[$num]=$((${Twithcomment[$num]}+1)) nom=$(expr "$file" : "\(.* [0-9]\+\|.* [0-9]\+_[0-9]\+\) ") #nom fichier sans comment if [ -z "$nom" ] ; then read -p "Erreur interne - fichier '$file' : commentaire pas trouvé. " else if [ $i -eq 1 ] ; then oldComment=$(expr substr "$file" $(expr length "$nom"+2) $(expr length "$file")) oldComment=${oldComment%.jpg} #supprime le suffixe echo -e "$nom \033[31m$oldComment\033[0m.jpg\t ==> $nom$espaceComment\033[32m$comment\033[0m.jpg" else move "$file" "$nom$espaceComment$comment.jpg" fi fi fi fi fi done if [ $i -eq 1 ] ; then nocomment=0 withcomment=0 [ ! "$DELETE" = 1 ] && for i in ${Tnocomment[@]}; do nocomment=$(($nocomment+$i)); done for i in ${Twithcomment[@]}; do withcomment=$(($withcomment+$i)); done [ "$DELETE" = 1 -a $withcomment -eq 0 ] && echo "Pas de photos avec commentaire trouvées entre $begin et $end !" && exit [ $nocomment -eq 0 -a $withcomment -eq 0 ] && echo "Pas de photos trouvées entre $begin et $end !" && exit echo -en "\033[31m" for ((n=$begin;n<$(($end+1));++n)); do l=$((${Tnocomment[$n]}+${Twithcomment[$n]}+0)) if [ $l -eq 0 ]; then [ -z "$beg" ] && echo -en "\nAttention :\t" || echo -en "\n\t\t" beg=1 echo -en "aucune photo ne porte le numéro \033[1m$n\033[0;31m" elif [ $l -ne 1 ]; then [ -z "$beg" ] && echo -en "\nAttention :\t" || echo -en "\n\t\t" beg=1 echo -en "$l photos portent le numéro \033[1m$n\033[0;31m" fi done echo -en "\033[0m" [ $nocomment -ne 0 ] && echo -en "\n > $nocomment commentaires à ajouter." [ $withcomment -ne 0 ] && echo -en "\n > $withcomment commentaires à modifier." read -p " Vous confirmez [O/n] ? " res if [ "$res" == "n" ] || [ "$res" == 'N' ]; then break fi rm $UNDO 2>/dev/null fi done exit } ############# FONCTION undo ############# UNDO=/tmp/renomme.undo undo() { [ ! -f $UNDO ] && echo "Pas de undo possible." && exit N=$(wc -l $UNDO | cut -f1 -d' ') for ((n=$(($N/2));n>0;--n)); do ligne_old=$(($n*2-1)) ligne_new=$(($n*2)) old_name=$(head -$ligne_old $UNDO|tail -1) new_name=$(head -$ligne_new $UNDO|tail -1) echo -e "$new_name\t==>\t$old_name" mv -i "$new_name" "$old_name" done rm $UNDO exit } ############# TRAITEMENT DES OPTIONS ############# #valeurs par défaut : optionLS="" espace=" " espaceComment=" " index=1 PREVIEW=1 while [ -n "$1" ]; do case "$1" in --chronologique|-c) optionLS="-tr";; #ls -tr : tri par date (-t) croissant(-r) --date|-d) DATE=1;; --nospace|-n) espace="" espaceComment="_";; --index|-i) index=$2 [ -n "$(echo $index | sed s/[0-9]*//)" -o -z "$index" ] && echo "Erreur, l'option \"$1\" doit être suivie d'un entier." && exit 1 shift;; --comment) COMMENT=1;; --diapo) DIAPO=1;; --dHP) DHP=1;; --dCANON) DCANON=1;; --noconfirm|-y) PREVIEW="";; --check) CHECK=1;; --affiche) AFFICHE=1;; --ajoutDate) AJOUTDATE=1;; --addComment|-a) shift && addComment "$@";; --supComment|-s) shift && addComment -s "$@";; --undo) undo;; --help|-h) usage;; *) #fin des options : #éventuellement dossier donné #puis : nom des photos (avec option --date) OU liste de fichiers if [ -d "$1" ]; then echo "Changement de dossier : $1" cd "$1" shift [ -n "$1" ] && nomPhotos="$1" else nomPhotos="$1" fi [ "$DATE" = 1 ] && shift && [ -n "$1" ] && echo -e "\n\033[7mL'option \"$*\" n'est pas attendue ici !\033[0m\n" && usage 1 break;; esac shift done if [ "$DHP" = "1" ] && [ "$DCANON" = "1" ] ; then echo -e "\n\033[7mL'option --dHP n'est pas compatible avec l'option --dCANON !\033[0m\n" usage 1 fi if [ "$DHP" = "1" -o "$DCANON" = "1" ] && [ "$CHECK" = "1" ] ; then echo -e "\n\033[7mL'option --dHP ou --dCANON n'est pas compatible avec l'option --CHECK !\033[0m\n" usage 1 fi if [ "$DATE" = "1" ] && [ "$optionLS" = "-tr" ] ; then echo -e "\n\033[7mL'option --date|-d n'est pas compatible avec l'option --chronologique|-c !\033[0m\n" usage 1 fi if [ "$CHECK" = "1" -o "$AFFICHE" = "1" -o "$AJOUTDATE" = "1" ] && [ "$DATE" != "1" ] ; then echo -e "\n\033[7mLes options --check, --affiche et --ajoutDate ne sont valables qu'avec l'option --date|-d!\033[0m\n" usage 1 fi ############# FONCTIONS POUR TRIER LES PHOTOS PAR DATE ############# quit() { rm $fichDate $fichSort $fichDif 2>/dev/null exit 0 } # écrit la date des fichiers concernés dans un fichier readDate() { ###################### ### LECTURE DATE ### ###################### fichDate=/tmp/date.renomme.tmp #date des photos + nom fichier fichSort=/tmp/dateSort.tmp #idem, trié fichDif=/tmp/dateDif.tmp #différences entre fichiers fichDate/Sort rm $fichDate 2>/dev/null #au cas où... motif="[jJ][pP][gG]" toutOK=0 #0 si on trouve une date valide pour toutes les photos, +1 sinon nombre=$(ls |grep "$nomPhotos.*\.$motif$" -c) n=0 if [ -z "$nombre" ] || [ $nombre -eq 0 ]; then echo "Pas de photos." quit fi for file in *"$nomPhotos"*.$motif ; do n=$(($n+1)) echo -en "[$n/$nombre] détection de la date de '$file'... " #date="$(identify -format %[EXIF:DateTimeOriginal] "$file")" date="$(exiftool -"DateTimeOriginal" -s -s -s "$file")" echo -en "\r\033[K" #retour en début de ligne + efface la ligne if [ -z "$date" ] ; then #-z : vide echo "Pas trouvé la date de la photo '$file'" toutOK=$(($toutOK+1)) elif [ $(expr length "$date") -ne 19 ] ; then echo "Format de la date de la photo '$file' non valide" toutOK=$(($toutOK+1)) else echo "$date>>>>$file" >> $fichDate if [ "$AFFICHE" == 1 ] ; then echo "$date : [$n/$nombre] $file" fi fi done if [ $nombre -eq $toutOK ] ; then echo "Plus de photos valides !" quit fi nombre2=$(grep "" $fichDate -c) if [ $(($toutOK+$nombre2)) -ne $nombre ] ; then echo "Erreur interne : nombre=$nombre ; nombre2=$nombre2 ; toutOK=$toutOK." quit fi nombre=$nombre2 if [ $toutOK -gt 0 ] ; then echo echo -e "\033[31mAttention, $toutOK/$(($toutOK+$nombre)) photos non prises en compte !\033[0m" fi } ############# DIAPORAMA ############# # photos de diaporama du HP : hrXX.....jpg ==renommées en==> hpim...._X.jpg if [ "$DHP" = "1" ]; then oldN=0 for file in *; do n=${file:3:1} #OU $(expr substr "$file" 4 1) if [ $n -eq $oldN ]; then seq=$(($seq+1)) else seq=1 fi oldN=$n numPhoto=${file:4:4} #OU =$(expr substr "$file" 5 4) move $file hpim${numPhoto}_${seq}.jpg done dcar=_ dname=hpim fi #photos de diaporama du CANON : st[abc..]_0123.jpg ==renommées en==> img_0123-[123].jpg if [ "$DCANON" = "1" ]; then for file in st*.jpg; do seq=${file:2:1} #seq=[abcd...] seq=$(( $(printf "%d" \'$seq) - $(printf "%d" \'a) + 1 )) #a=>1 ; b=>2 ; ... seq=$(printf %02d $seq) #1 => 01 ; 2 => 02 ; ... numPhoto=${file:4:4} #OU =$(expr substr "$file" 5 4) move $file img_${numPhoto}-${seq}.jpg done dcar=- dname=img_ fi ############# TYPE ET NOMBRE DE FICHIERS ############# if [ "$DATE" = "1" ]; then #tri de photos par date readDate ############# ### TRI ### ############# sort $fichDate > $fichSort diff $fichDate $fichSort > $fichDif #diff renvoie 0 si les mêmes fichiers, 1 si qq différences ou 2 sinon if [ $? -eq 0 ] ; then echo -e "\033[32mLes $nombre photos sont bien triées par date.\033[0m" [ "$CHECK" == 1 ] && quit read -p "Voulez-vous quand même les renommer [o/N] ? " res if [ ! "$res" == "o" ] && [ ! "$res" == 'O' ]; then quit fi else echo -e "\033[31mLes $nombre photos ne sont pas triées par date.\033[0m" [ "$CHECK" == 1 ] && quit fi # Nom des photos : titre=$(head -1 $fichSort|sed "s/.*>>>>//") #1ère ligne titre=${titre%.*} #supprime extension titre=${titre% [0-9]*} #supprime le plus petit suffixe read -p "Nom des photos ['$titre' par défaut ] : " res if [ "$res" != "" ]; then titre=$res fi #fichSort contient la liste des fichiers triés par date #si AJOUTDATE => contient aussi la date [ "$AJOUTDATE" != "1" ] && sed -i "s/.*>>>>//" $fichSort type="photos" liste="more $fichSort" else if [ $# -eq 0 ] ; then #Photos uniquement type="photos" motif="[jJmM][pP][gG]" #par défaut : photos et vidéos set *.$motif nombre=$(ls |grep "\.$motif$" -c) else type="fichiers" nombre=0 for file in "$@" ; do if [ -f "$file" ] ; then #si c'est un fichier ordinaire nombre=$(($nombre+1)) else if ! [ -d "$file" ] ; then echo "Ce fichier n'existe pas : $file" fi fi done fi if [ $nombre -eq 0 ] ; then echo "Aucun fichier trouvé." exit fi liste="ls -1 $optionLS -d \"\$@\"" [ "$optionLS" = "-tr" ] && echo "Tri suivant la date des fichiers :" || echo "Tri suivant l'ordre alphabétique :" read -p "> $nombre $type à renommer. Nom des $type : " titre fi if [ -z "$DIAPO" ] && [ -n "$(eval $liste | grep ".* [0-9]\+_[0-9]\+[ \.]")" ]; then echo -en "Il semble qu'il y ait des photos de diaporama (genre 'photo 45\033[1;7m_2\033[0m.jpg') à\nrenommer. Voulez-vous activez l'option '--diapo' [O/n] ? " read res if [ ! "$res" == "n" ] && [ ! "$res" == 'N' ]; then DIAPO=1 fi fi n_digit=$(expr length $(($nombre+index-1))) #nombre de digit (dans nom fichier) n_digitAffich=$(expr length $nombre) #nombre de digit (pour affichage) ############# RENOMMAGE ############# for i in 1 2 ; do n=$(($index-1)) #n° fichier nAffich=0 #n° pour affichage uniquement IFS=$'\n' for file in $(eval $liste) #OU : eval $liste|while read file (mais pb avec mv -i (si read) et si espace en début de fichier) do if [ "$AJOUTDATE" == "1" ]; then date=$(echo "$file"|sed "s/\([0-9]*\):\([0-9]*\):\([0-9]*\).*/\3-\2-\1/") addDate="$espaceComment($date)" file=$(echo "$file"|sed "s/.*>>>>//") fi if [ ! -f "$file" ] && [ ! "$DATE" = "1" ] ; then continue #ex. : dossier fi #extension ext=${file##*.} if [ "$ext" = "$file" ] ; then #pas d'extension ext="" else ext=".$ext" fi if [ $type = "photos" ] ; then ext=`echo $ext | tr A-Z a-z` #extension du fichier en minuscules fi #traitement des diaporamas ($file de la forme HP : hpim0045_5.jpg ; CANON : img_0045-05.jpg) if [ "$DHP" = "1" -o "$DCANON" = "1" ]; then beginDiap=$(expr index "$file" $dcar) #0 si pas trouvé if [ $beginDiap -ne 0 ] && [ "${file:0:4}" == $dname ] ; then endDiap=$(expr index "$file" .) numDiap=${file:$beginDiap:$(($endDiap-$beginDiap-1))} #position (comptée à partir de 0):longueur ajout="_$numDiap" if [ $numDiap -ne 1 ]; then n=$((n-1)); fi else ajout="" fi fi #fin traitement des diaporamas #traitement des commentaires #ajout d'un éventuel commentaire dans le nom de la photo #lorsque le nom est du type 'noël 012 Farnières.jpg' ==> 'Farnières' #OLD : comFile=${file#* [0-9]* } #supprime le préfixe (va pas bien si chiffre avant) #OLD : if [ "$comFile" == "$file" ] ; then #y'a pas de commentaires if [ "$COMMENT" = 1 ] ; then if [ "$DIAPO" = 1 ] ; then index_com=$(expr "$file" : ".* [0-9]\+ .\|.* [0-9]\+_[0-9]\+ .") else index_com=$(expr "$file" : ".* [0-9]\+ .") fi if [ $index_com -eq 0 ] ; then comFile="" else comFile=$(expr substr "$file" $index_com $(expr length "$file")) comFile=${comFile%$ext} #supprime le suffixe comFile="$espaceComment$comFile" fi fi #fin traitement des commentaires #traitement des diaporamas ($file de la forme 'photo xxx_yy.jpg') #ajout d'un éventuel suffixe au n° de la photo #lorsque le nom est du type 'photo 012_5 Farnières.jpg' ==> '_5' if [ "$DIAPO" = 1 ] ; then numDiap=$(expr "$file" : ".* [0-9]\+_\([0-9]\+\)[ \.]") #renvoie ce qui matche dans () ; vide sinon if [ -z "$numDiap" ]; then ajout="" else ajout="_$numDiap" if [ $numDiap -ne 1 ]; then n=$((n-1)); fi fi fi #fin traitement des diaporamas n=$(($n+1)) nAffich=$(($nAffich+1)) n0=$(printf %.${n_digit}d $n) #$n précédé de 0 nAffich0=$(printf %.${n_digitAffich}d $nAffich) #$nAffich précédé de 0 nom="${titre}${espace}${n0}${ajout}$addDate$comFile${ext}" nomAffich="${titre}${espace}\033[1m${n0}${ajout}\033[0;32m$addDate$comFile\033[0m${ext}" echo -en "\r\033[K" if [ ! "$file" = "$nom" ] ; then echo -en "[$nAffich0/$nombre] $file\t==>\t$nomAffich " if [ -z "$PREVIEW" ] || [ $i -eq 2 ]; then addTMP=".TMPTMP" #pour ne pas donner le nom d'une photo qui existerait déjà move "$file" "$nom" "$addTMP" fi else echo -en "[$nAffich0/$nombre] \033[37m$file\t==>\t(inchangé)\033[0m" fi [ $i -eq 1 ] && echo done [ -z "$PREVIEW" ] && break if [ $i -eq 1 ]; then echo read -p "Vous confirmez [O/n] ? " res if [ "$res" == "n" ] || [ "$res" == 'N' ]; then quit fi else echo -en "\r\033[K" fi done [ -z "$(ls | grep "\.TMPTMP$")" ] && quit #cas où aucune photo renommée echo -en "Finalisation du renommage... " for fileTMP in *.TMPTMP; do file="${fileTMP%.*}" [ -f "$file" ] && echo "Conflit de nommage : le fichier '$file' existe déjà." mv -i "$fileTMP" "$file" done echo -en "\r\033[K" quit