#!/usr/bin/ksh ##### # ! /bin/bash ############################################################################### # # # allcompare.sh : Check for mutliple identical files. # # Give a list of file identical. # # # # +---------------------------------------------------------------------+ # # | Copyright (c) 2007-2008 Flyounet | # # +---------------------------------------------------------------------+ # # | This program is free software; you can redistribute it and/or | # # | modify it under the terms of the GNU General Public License (GPL) | # # | as published by the Free Software Foundation; either version 2 | # # | of the License, or (at your option) any later version. | # # | The GPL can be found at http://www.gnu.org/licenses/gpl.html | # # +---------------------------------------------------------------------+ # # | Author: Flyounet Initiale Release # # > Not finished Yet. # # v0.02 [20/12/2007] Flyounet : # # > Ajout de l'entete de script pour la gestion de version. # # v0.03 [28/12/2007] Flyounet : # # > Ca prend forme l'ensemble des fichiers est trouve. La partie selec- # # tion du FS par rapport au disk fonctionne bien. # # v0.04 [28/12/2007] Flyounet : # # * sha1Verif & md5Verif ajout de l'export de _hashExt. # # v0.05 [31/12/2007] Flyounet : # # * doublonUnify : Mais elle est toute foireuse cette fonction... Tu # # SORS ! Tu reviens plus tard et tu te soignes... Et tu reviens finir # # après... # # > Et j'ai scripte, scripte e en shell et puis en perl, et j'ai pleure # # pleure e, j'avais bien trop de peine... # # v0.06 [31/12/2007] Flyounet : # # * doublonAnalyse & doublonParse sont corrigees. Comme un boulet j'ai # # oublie de supprimer les fichiers ok apres utilisation... Une mise en # # vrac de folie. # # * fileSearchFS : le quoting-style des fichiers a ete supprime du find. # # Ca faisait bouse doublonParse. # # > C'est l'heure de rentrer pour le reveillon alors j'arrete pour ce # # soir. On verra demain. # # * doublonUnify : Faut tout refaire la dedans. Elle pue grave. # # v0.07 [01/01/2008] Flyounet : # # * doublonParse & doublonUnify : La taille du fichier a ete ajoutee en # # premier element du fichier. # # v0.08 [02/01/2008] Flyounet : MST # # > First version fully fonctionnal ! # # + Flags -K and -Q have been added. # # v0.09 [02/01/2008] Flyounet : MST # # > In some case, files have accent or some crazy characters and the sed # # part of fileSearchFS doesn't reconize the pattern. So the # # * fileSearch : Errors are now grepped and reinjected to be treated. # # v0.10 [07/01/2008] Flyounet : Verole # # + Un increment est ajoute pour identifier chaque groupe identique. # # v0.11 [11/01/2008] Flyounet : Sida # # + Parameters -i & -I have been added to control size of doublons. # # v0.12 [22/01/2008] Flyounet : VIH # # * -I now work... I forget to try it :( # # v0.13 [22/01/2008] Flyounet : Blennorragie # # + Parameter -b has been added to control -i and -I if -b is used then # # old style is used : Find all file and after work on size. Without # # -b parameter : find only file which are minus or greater than the # # size given with -i and -I. # # -b is usefull if the 'find' command can't use the '-size' argument. # # v0.14 [26/01/2008] Flyounet : Blennorragie # # * fileSearchFS : _bytesUnder was used for the -i parameters instead of # # _bytesOver. # # v0.15 [08/04/2008] Flyounet : Blennorragie # # > Some variables have been removed... A poor copy/paste in the early # # stage of dev... # # # # # # # # # # # # # # # # # # # # # # # VERSION=0.15 PROTOCOL=0.01 ############################################################################### # # # Todo : # # OK Changer les preferences. Par defaut on utilise SHA1SUM et si l'utilisa- # # teur le veut, il forcera md5sum. Faire le test avec sha1 et md5 c'est # # trop long/violent. Ca permettra aussi de simplifié le code # # OK Quand on check chacun des fichiers faire un compteur qui indique le # # pourcentage terminé. # # < Permettre de ne pas prendre en compte des fichiers ou des repertoires. # # < Il faudrait afficher le nombre de fichiers de chaque FS. # # < Modifier l'affichage en cas d'utilisation -i ou -I # # < C'est foireux le compte ajoute en v0.10 et pas lisibile en plus... # # < Pour les fichiers > 10M faire un check du 1er MiB et si != ==> files != # # < files > 10M check 1er MiB == ==> Check last MiB et si != ==> files != # # < Fournir un argument pour gere les deux requetes de dessus. # # < Make a real french translation and another for english. # # < Faire une interface graphique... # # # # # ############################################################################### # # # Legende : # # + --> Indique une nouveaute, un ajout de fonctionnalite. # # * --> Indique une correction de bogue. # # - --> Indique la suppression d'une fonctionnalite/variable. # # > --> Indique une information n'ayant pas forcement de rapport avec le code# # < --> Indique une amelioration a apporter au code. # # <- --> Indique une amelioration en cours de developpement/realisation. # # OK --> Indique qu'une amelioration a ete effectuee. # # # ############################################################################### #set -xv ############################################################################### # # # Les Variables # # # ############################################################################### # ############################################################ # _oldFashionCount=0 _parallele=1 _md5use=0 _sha1use=1 _recursive=1 _quiet=0 _remove=1 _pid=$$ _name="`basename ${0}`" _bytesUnder=0 _bytesOver=0 fileFSuse=0;fileOutputuse=0;varFSuse=0;fileTempuse=0; ############################################################################### # # # Les Fonctions # # # ############################################################################### ########################################################### help () { echo "${ownName} [-h] [-P] [-M|-S] [-R] [-i bytes] [-I bytes] [-o filename] [-t directory] [-f filename|-s FS]*" echo " -h this help" echo " -K do not remove temporary folder at end" echo " -Q do not write any information (quiet mode) (only errors will be written)" echo " -P do not parallelize research" echo " -M use md5sum for file analyze" echo " -S use sha1sum for file analyze (default)" echo " -R don't search recursivly" echo " -i bytes don't search files that have more than -i bytes" echo " -I bytes don't search files that have less than -I bytes" echo " -o filename write results to this file" echo " -t directory use this directory as prefix for temporary files" echo " -f filename find FS in the file instead of argument" echo " -s FS filesystem are separated by a semi colon" echo " * Mandatory field" exit 0 } ########################################################### # Fonction de gestion des erreurs myError () { case ${1} in 0Analyse) echo "You cannot use -M and -S at the same time !"; help;; fileOrNot) echo "You connot use -f and -s at the same time !"; help;; noFile_noArgs_noGlory) echo "You MUST use -f OR -s argument !";help;; *) echo "${1}";; esac } ########################################################### # Parse les arguments # -h l'aide # -b Ancienne façon de chercher les fichiers par taille # -f fourni un fichier avec les FSs # -i ne traite pas les fichiers de plus de tant d'octets. # -I ne traite pas les fichiers de moins de tant d'octets. # -K ne supprime pas le repertoire temporaire en fin de traitement. # -Q n'affiche plus les infos : Mode Quiet # -P ne parralelise pas les find et sum # -M utilise md5sum # -S utilise sha1sum # -o fichier de sortie # -t fichier temporaire [[ $# -lt 1 ]] && help _argsAreCool=0 while getopts ":f:s:t:o:i:I:hH?PMSRKQb" option; do case ${option} in f) fileFS="${OPTARG}";fileFSuse=1;[[ ${varFSuse} -eq 1 ]] && myError 'fileOrNot';_argsAreCool=1;; b) _oldFashionCount=1;; i) _bytesOver=${OPTARG};; I) _bytesUnder=${OPTARG};; K) _remove=0;; Q) _quiet=1;; P) _parallele=0;_argsAreCool=1;; M) _md5use=1;_sha1use=0;_argsAreCool=1;; S) _sha1use=1;[[ ${_md5use} -eq 1 ]] && myError '0Analyse';_argsAreCool=1;; R) _recursive=0;_argsAreCool=1;; o) fileOutput="${OPTARG}";fileOutputuse=1;_argsAreCool=1;; s) varFS="${OPTARG}";varFSuse=1;[[ ${fileFSuse} -eq 1 ]] && myError 'fileOrNot';_argsAreCool=1;; t) fileTemp="${OPTARG}";fileTempuse=1;_argsAreCool=1;; ?) help;; H) help;; h) help;; *) help;; esac done [[ ${_argsAreCool} -eq 0 ]] && help [[ ${varFSuse} -eq 0 ]] && [[ ${fileFSuse} -eq 0 ]] && myError 'noFile_noArgs_noGlory'; ########################################################### # Verification pour voir si md5sum et sha1sum sont presents md5Verif () { which md5sum > /dev/null if [ $? -ne 0 ] then myError "Attention md5sum n'est pas present dans le path ou sur le systeme !" exit 10 fi export _hashExt='md5' } sha1Verif () { which sha1sum > /dev/null if [ $? -ne 0 ] then myError "Attention sha1sum n'est pas present dans le path ou sur le systeme !" exit 10 fi export _hashExt='sha1' } ########################################################### fileOutputVerif () { if [ ${fileOutputuse} -eq 1 ] then _fileOutput="${fileOutput}" else _fileOutput="${_name}.resutls" fi if [ -f "${_fileOutput}" ] then if [ -w "${_fileOutput}" -a -r "${_fileOutput}" ] then > "${_fileOutput}" else myError "Can't write/read file ${_fileOutput}" exit 11 fi else touch "${_fileOutput}" if [ $? -ne 0 ] then myError "Can't create ${_fileOutput}" exit 11 fi fi export _fileOutput } ########################################################### fileTempVerif () { if [ ${fileTempuse} -eq 1 ] then _dirTemp="${fileTemp}" else _dirTemp="/tmp/${_name}.${_pid}" fi if [ -d "${_dirTemp}" ] then if [ -x "${_dirTemp}" -a -w "${_dirTemp}" -a -r "${_dirTemp}" ] then touch "${_dirTemp}" else myError "Can't write/read/execute directory ${_dirTemp}" exit 11 fi else mkdir "${_dirTemp}" if [ $? -ne 0 ] then myError "Can't create ${_dirTemp}" exit 11 fi fi export _dirTemp } ########################################################### # Fonction qui verifie si le fichier fileFS est lisible fileFSVerif () { if [ ! -f "${fileFS}" -o ! -r "${fileFS}" ] then myError "file ${fileFS} doesn't exist or can't be read !" exit 11 fi } ########################################################### # Fonction qui verifie que carFS n'est pas vide varFSVerif () { if [ `echo -n "${varFS}" | wc -m | awk '{print $1}'` -lt 1 ] then myError "Argument of -s parameter is inconsistant '${varFS}'" exit 11 fi } ########################################################### argsVerif () { [[ ${fileFSuse} -eq 1 ]] && fileFSVerif [[ ${varFSuse} -eq 1 ]] && varFSVerif fileOutputVerif fileTempVerif } ########################################################### # Lancement de toutes les Verifications allVerification () { [[ ${_md5use} -eq 1 ]] && md5Verif [[ ${_sha1use} -eq 1 ]] && sha1Verif argsVerif } ########################################################### fileFSParse () { _ligne=1 while read a do if [ ! -d ${a} -o ! -r ${a} ] then myError "${fileFS}:ligne ${_ligne} : ${a} doesn't exist or can't be read !" exit 11 fi _disk="`df \"${a}\" | tail -1 | awk '{print $1}'`" echo "${_disk} ${a}" >> "${_dirTemp}/fsdf" let _ligne=${_ligne}+1 done < "${fileFS}" } ########################################################### varFSParse () { _arg=1 echo "${varFS}" | sed 's/::/\n/g' > "${_dirTemp}/.ultra.tmp" while read a do if [ ! -d ${a} -o ! -r ${a} ] then myError "${varFS}:Argument ${_arg} : ${a} doesn't exist or can't be read !" exit 11 fi _disk="`df \"${a}\" | tail -1 | awk '{print $1}'`" echo "${_disk} ${a}" >> "${_dirTemp}/fsdf" let _arg=${_arg}+1 done < "${_dirTemp}/.ultra.tmp" rm -f "${_dirTemp}/.ultra.tmp" } ########################################################### fsTri () { _nbFS=0 if [ ${_parallele} -eq 0 ] then cat "${_dirTemp}/fsdf" | awk '{print $2}' | sort > "${_dirTemp}/fs1" _nbFS=1 else for i in `cat "${_dirTemp}/fsdf" | awk '{print $1}' | sort | uniq` do let _nbFS=${_nbFS}+1 grep -E "^${i} " "${_dirTemp}/fsdf" | awk '{print $2}' > "${_dirTemp}/fs${_nbFS}" done fi export _nbFS } ########################################################### fileSearchFS () { while read a do #set -xv _tmpSizeOver="" _tmpSizeUnder="" if [ ${_oldFashionCount} -eq 0 ] then if [ ${_bytesUnder} -gt 0 ] then _tmpSizeUnder="-size +${_bytesUnder}c " fi if [ ${_bytesOver} -gt 0 ] then _tmpSizeOver="-size -${_bytesOver}c " fi fi #find ${a} -type f -exec ls -la -Q --quoting-style=shell -- {} \; >> "${_dirTemp}/fs${1}.txt" # Good LIne : find ${a} -type f -exec ls -la -- {} \; >> "${_dirTemp}/fs${1}.txt" find ${a} -type f ${_tmpSizeUnder}${_tmpSizeOver} -exec ls -la -- {} \; >> "${_dirTemp}/fs${1}.txt" #find ${a} -type f -exec ls -la -- {} \; >> "${_dirTemp}/fs${1}.txt" #sed "s/^\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*-[0-9]*-[0-9]*[[:space:]][0-9]*:[0-9]*\)[[:space:]]*\(.*\)$/\5 '\7'/g" "${_dirTemp}/fs${1}.txt" | sed "s/^\([0-9]*\)[[:space:]]''\(.*\)''$/\1 \2/g" > "${_dirTemp}/fs${1}.ref" sed "s/^\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*-[0-9]*-[0-9]*[[:space:]][0-9]*:[0-9]*\)[[:space:]]*\(.*\)$/\5 \7/g" "${_dirTemp}/fs${1}.txt" | sed "s/^\([0-9]*\)[[:space:]]''\(.*\)''$/\1 \2/g" > "${_dirTemp}/fs${1}.ref" #sed "s/^\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([^ ]*\)[[:space:]]*\([0-9]*\)[[:space:]]*\([0-9]*-[0-9]*-[0-9]*[[:space:]][0-9]*:[0-9]*\)[[:space:]]*\(.*\)$/\5 '\7'/g" "${_dirTemp}/fs${1}.txt" > "${_dirTemp}/fs${1}.ref" [[ ${_quiet} -eq 0 ]] && echo " sur ${a}... Done" sleep 1 #set +xv done < "${_dirTemp}/fs${1}" > "${_dirTemp}/fs${1}.ok" } ########################################################### fileSearch () { #set -xv _incr=0 while [ ${_incr} -ne ${_nbFS} ] do let _incr=${_incr}+1 fileSearchFS ${_incr} & done while [ `ls -1 ${_dirTemp}/fs*.ok 2>/dev/null | wc -l` -ne ${_nbFS} ] do sleep 5 done _incr=0 > "${_dirTemp}/fsall.err" while [ ${_incr} -ne ${_nbFS} ] do let _incr=${_incr}+1 # Try to correct the incorrect line parsed with sed #cat "${_dirTemp}/fs${_incr}.ref" >> "${_dirTemp}/fsall.ref" grep -E '^[0-9]* ' "${_dirTemp}/fs${_incr}.ref" >> "${_dirTemp}/fsall.ref" grep -vE '^[0-9]* ' "${_dirTemp}/fs${_incr}.ref" >> "${_dirTemp}/fsall.err" done while read a b c d e f g h do echo "${e} ${h}" >> "${_dirTemp}/fsall.ref" done < "${_dirTemp}/fsall.err" } ########################################################### doublonSearch () { # Nb Files [[ ${_quiet} -eq 0 ]] && echo -n "Files: `wc -l "${_dirTemp}/fsall.ref" | awk '{print $1}'` - " awk '($1 == 0) {print $0}' "${_dirTemp}/fsall.ref" > "${_dirTemp}/fsall.zero" # Nb Files 0 byte [[ ${_quiet} -eq 0 ]] && echo -n "0byte: `wc -l "${_dirTemp}/fsall.zero" | awk '{print $1}'` - " awk '($1 != 0) {print $1}' "${_dirTemp}/fsall.ref" | sort -rn | uniq -u > "${_dirTemp}/fsall.uniq" # Nb uniq files (no doublon) [[ ${_quiet} -eq 0 ]] && echo -n "Uniques: `wc -l "${_dirTemp}/fsall.uniq" | awk '{print $1}'` - " # Doublons awk '($1 != 0) {print $1}' "${_dirTemp}/fsall.ref" | sort -rn | uniq -d > "${_dirTemp}/fsall.doub" #echo -n "Doublons: `wc -l "${_dirTemp}/fsall.doub" | awk '{print $1}'` ... " if [ ${_oldFashionCount} -ne 0 ] then if [ ${_bytesUnder} -gt 0 ] then awk -vnb=${_bytesUnder} '($1 > nb) {print}' "${_dirTemp}/fsall.doub" > "${_dirTemp}/fsall._tmp" cat "${_dirTemp}/fsall._tmp" > "${_dirTemp}/fsall.doub" rm -f "${_dirTemp}/fsall._tmp" fi if [ ${_bytesOver} -gt 0 ] then awk -vnb=${_bytesOver} '($1 < nb) {print}' "${_dirTemp}/fsall.doub" > "${_dirTemp}/fsall._tmp" cat "${_dirTemp}/fsall._tmp" > "${_dirTemp}/fsall.doub" rm -f "${_dirTemp}/fsall._tmp" fi fi _doublons="`wc -l \"${_dirTemp}/fsall.doub\" | awk '{print $1}'`" [[ ${_quiet} -eq 0 ]] && echo -n "Doublons: ${_doublons} - " export _doublons [[ ${_quiet} -eq 0 ]] && echo -n "Errors: `wc -l \"${_dirTemp}/fsall.err\" | awk '{print $1}'` ... " } ########################################################### doublonParse () { grep -E "^${2} " "${_dirTemp}/fs${1}.ref" > "${_dirTemp}/dbl${1}.txt" #echo " doublonParse ${1} ${2} cat ${_dirTemp}/dbl${1}.txt : `cat ${_dirTemp}/dbl${1}.txt`" > "${_dirTemp}/dbl${1}.${_hashExt}" _cpt="`cat "${_dirTemp}/dbl${1}.cpt"`" while read a b do echo -n "${2} " >> "${_dirTemp}/dbl${1}.${_hashExt}" [[ ${_md5use} -ne 0 ]] && md5sum "${b}" >> "${_dirTemp}/dbl${1}.${_hashExt}" [[ ${_sha1use} -ne 0 ]] && sha1sum "${b}" >> "${_dirTemp}/dbl${1}.${_hashExt}" let _cpt=${_cpt}+1 done < "${_dirTemp}/dbl${1}.txt" #echo " doublonParse arg:${1} arg:${2} cpt:${_cpt} cat ${_dirTemp}/dbl${1}.${_hashExt} : `cat ${_dirTemp}/dbl${1}.${_hashExt}`" echo "${_cpt}" > "${_dirTemp}/dbl${1}.cpt" > "${_dirTemp}/dbl${1}.ok" } ########################################################### doublonUnify () { # awk '{print $2}' "${_dirTemp}/dblall.${_hashExt}" | sort | uniq -d > "${_dirTemp}/dblall.${_hashExt}.doub" # > "${_dirTemp}/dblall.${_hashExt}.res" #set -xv #_x=1 for i in `awk '{print $2}' "${_dirTemp}/dblall.${_hashExt}" | sort | uniq -d` do grep -E "^([0-9])* ${i} " "${_dirTemp}/dblall.${_hashExt}" > "${_dirTemp}/dblall.${_hashExt}.res" _nbfiles="`wc -l \"${_dirTemp}/dblall.${_hashExt}.res\" | awk '{print $1}'`" if [ ${_nbfiles} -ne 0 ] then echo "" >> ${_fileOutput} echo "################################################################################" >> ${_fileOutput} echo -n "${_x}) ${_nbfiles} fichiers de " >> ${_fileOutput} _trut=0 while read a b c do if [ ${_trut} -eq 0 ] then echo "${a} octets [hash : ${b}]" >> ${_fileOutput} fi let _trut=${_trut}+1 echo "${_trut}) ${c}" >> ${_fileOutput} done < "${_dirTemp}/dblall.${_hashExt}.res" let _x=${_x}+1 export _x fi done #set +xv } ########################################################### doublonAnalyse () { #set -xv # Genere les fichiers de compteurs _incr=0 export _x=1 while [ ${_incr} -ne ${_nbFS} ] do let _incr=${_incr}+1 echo '0' > "${_dirTemp}/dbl${_incr}.cpt" done # Pour chaque taille de fichier en doublon _pourcentage=0 _pourcentage_old=0 [[ ${_quiet} -eq 0 ]] && echo -n "0%" while read a do #echo "doublonAnalyse $a" _incr=0 while [ ${_incr} -ne ${_nbFS} ] do let _incr=${_incr}+1 #echo "doublonParse incr:${_incr} size:${a} :" doublonParse ${_incr} ${a} & done # Attente while [ `ls -1 ${_dirTemp}/dbl*.ok 2>/dev/null | wc -l` -ne ${_nbFS} ] do #echo "doublonAnalyse size:${a} nb ok : `ls -1 ${_dirTemp}/dbl*.ok 2>/dev/null | wc -l`" sleep 2 done #echo "doublonAnalyse size:${a} Concat..." # Concatenation > "${_dirTemp}/dblall.${_hashExt}" _incr=0 while [ ${_incr} -ne ${_nbFS} ] do let _incr=${_incr}+1 rm -f -- "${_dirTemp}/dbl${_incr}.ok" cat "${_dirTemp}/dbl${_incr}.${_hashExt}" >> "${_dirTemp}/dblall.${_hashExt}" done #echo "doublonAnalyse size:${a} nb ok : `ls -1 ${_dirTemp}/dbl*.ok 2>/dev/null | wc -l`" #echo "doublonAnalyse size:${a} cat ${_dirTemp}/dblall.${_hashExt} : `cat ${_dirTemp}/dblall.${_hashExt}`" #sleep 1 doublonUnify #set -xv let _pourcentage=${_pourcentage}+1 let _pourcent=${_pourcentage}*100/${_doublons} if [ ${_pourcent} -ne 0 ] then if [ ${_pourcent} -ne ${_pourcentage_old} ] then let _mod=${_pourcent}%5 if [ ${_mod} -eq 0 ] then [[ ${_quiet} -eq 0 ]] && echo -n "${_pourcent}%" else [[ ${_quiet} -eq 0 ]] && echo -n "." fi fi _pourcentage_old=${_pourcent} fi #set +xv done < "${_dirTemp}/fsall.doub" } ########################################################### # MAIN FUNCTION [[ ${_quiet} -eq 0 ]] && echo -n "+ Verifications... " allVerification [[ ${_quiet} -eq 0 ]] && echo "Done" [[ ${_quiet} -eq 0 ]] && [[ ${_sha1use} -eq 1 ]] && echo " - sha1sum will be used." [[ ${_quiet} -eq 0 ]] && [[ ${_md5use} -eq 1 ]] && echo " - md5sum will be used." [[ ${_quiet} -eq 0 ]] && echo " - Destination file will be : ${_fileOutput}" [[ ${_quiet} -eq 0 ]] && echo " - Temp. dir. will be ${_dirTemp}" [[ ${_quiet} -eq 0 ]] && echo -n "+ Analyse arguments... " [[ ${fileFSuse} -eq 1 ]] && fileFSParse [[ ${varFSuse} -eq 1 ]] && varFSParse [[ ${_quiet} -eq 0 ]] && echo "Done" [[ ${_quiet} -eq 0 ]] && echo -n "+ Tri des FS... " fsTri [[ ${_quiet} -eq 0 ]] && echo "Done" [[ ${_quiet} -eq 0 ]] && echo "+ Recherche des fichiers..." fileSearch [[ ${_quiet} -eq 0 ]] && echo " Recherche des fichiers... Done" [[ ${_quiet} -eq 0 ]] && echo -n "+ Recherche des doublons par taille... " doublonSearch [[ ${_quiet} -eq 0 ]] && echo "Done" [[ ${_quiet} -eq 0 ]] && echo -n "+ Analyse des doublons... " doublonAnalyse [[ ${_quiet} -eq 0 ]] && echo " Done" [[ ${_remove} -ne 0 ]] && rm -rf -- "${_dirTemp}" exit 0