手動だとファイル選びが面倒なバックアップ処理を、起動するだけで全てオートでやってくれるマクロです。
//BackUp_Proc.ijm
version = "2.9.0";
print("");
print("backUp Processing");
print("ver",version);
startTime = whatTimeNow();
//directory setting
srcDir = "/Volumes/StorageA/Data/";
dstDir = "/Volumes/StorageB/BackUp/";
oldDataDir = "/Volumes/StorageB/oldData/";
nazoDataDir = "/Volumes/StorageB/nazoData/";
//search directory
print("");
print("Source : ",srcDir);
print("Destination : ",dstDir);
print("");
flagDirExists = 1;
if(File.exists(srcDir)){
print("srcDir...exists");
}else{
print("srcDir...could not be found");
flagDirExists = 0;
}
if(File.exists(dstDir)){
print("dstDir...exists");
}else{
print("dstDir...could not be found");
flagDirExists = 0;
}
if(File.exists(oldDataDir)){
print("oldDataDir...exists");
}else{
print("oldDataDir...could not be found");
flagDirExists = 0;
}
if(File.exists(nazoDataDir)){
print("nazoDataDir...exists");
}else{
print("nazoDataDir...could not be found");
flagDirExists = 0;
}
if(flagDirExists == 0) exit("Error!! Directory ga Naiyo!!");
//variable setting
var totalFiles,flagEmpty,flagMissing;
var newCount = 0;
var updateCount = 0;
var deleteCount = 0;
var nazoCount = 0;
print("");
//File Counts of preBackUp in Source dir
print("Counting totalFiles of srcDir...");
totalFiles=0;
flagMissing=0;
countFiles(srcDir);
countSrc = totalFiles;
print(totalFiles,"Files");
print("");
//main operation
//check S dir
print("*** Loop in Source directory - (Checking Updated File)***");
listFilesSrc(srcDir,dstDir);
//Check D Dir
print("");
print("*** Loop in Destination directory part1- (Checking Deleted File)***");
listFilesDst(srcDir,dstDir);
//seek and delete disappear Directory in D Dir
print("");
print("*** Loop in Destination directory part2 - (Checking Deleted Directory)***");
seekDeletedDir(srcDir,dstDir);
//File Counts of postBackUp in destination dir
print("");
print("Counting totalFiles of dstDir...");
totalFiles = 0;
countFiles(dstDir);
countDst = totalFiles;
print(totalFiles,"Files");
print("");
//Check Empty Directory (File.delete...parentDir ga kesenai koto aru)
flagEmpty = 0;
print("");
print("------Suspicious of File.delete Error (Empty dir)------");
emptyDir(srcDir,dstDir);
if(flagEmpty == 0) print(" None");
print("-------------------------------------------------------");
//check file counts
print("");
print("FileCount in Source = ",countSrc);
print("FileCount in Destination = ",countDst);
if(countSrc == countDst) {
print("...verified");
print("");
print("backUp consistency ...");
validateSrc(srcDir,dstDir);
validateDst(srcDir,dstDir);
if(flagMissing == 1){
print("!!!!!!!!! Check above file location !!!!!!!!!");
exit("Error!!!! The File consistency is Mismatch! Check Missing Files!");
}else if(flagMissing == 0){
print("... varidated");
wait(1000);
}
}
if(countSrc != countDst) {
print("");
print("!!!!!!!!! Check this file location !!!!!!!!!");
validateSrc(srcDir,dstDir);
validateDst(srcDir,dstDir);
exit("Error!!!! The FileCount is Mismatch! Check Missing Files!");
}
//fin
finishTime = whatTimeNow();
print("");
print("New File = ",newCount);
print("Update File = ",updateCount);
print("Delete File = ",deleteCount);
print("Nazo File = ",nazoCount);
print("");
print("Start Time .... ",startTime);
print("FinishTime ... ",finishTime);
print("oshimai");
beep();
showMessage("Complete successfully");
//-----------------------------------------------------------------------------
//Define listFilesSrc (loop in srcDir)
function listFilesSrc(dirS,dirD){
listS = getFileList(dirS);
for(i=0; i<listS.length; i++){
if(endsWith(listS[i],"/")){
//Make New Directory in dstDir
if(!File.exists(dirD+listS[i])){
File.makeDirectory(dirD+listS[i]);
print("mkDir : ",dirD+listS[i]);
}
//Exists in dstDir
listFilesSrc(dirS+listS[i],dirD+listS[i]);
}else{
//If the object is File
checkFilesSrc(dirS+listS[i],dirD+listS[i]);
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function checkFilesSrc(pathS,pathD){
//exists both S and D
if(File.exists(pathD)){
//update Time (source)
timeS = File.lastModified(pathS);
timeS = parseFloat(timeS);
//update Time (destination)
timeD = File.lastModified(pathD);
timeD = parseFloat(timeD);
//detect updated File
if(timeS > timeD){
//copy D file to old
File.copy(pathD,oldDataDir+getTimeStampAlpha()+"_"+listS[i]);
print(i,"-","Move Old Data to : ",oldDataDir+getTimeStampAlpha()+"_"+listS[i]);
//copy S file to D
File.copy(pathS,pathD);
print(i,"-","Copy Updated Data to : ",pathD);
updateCount++;
}
//detect updated File in dstDir (nazo Data)
if(timeS < timeD){
//copy D file to nazo
File.copy(pathD,nazoDataDir+getTimeStampAlpha()+"_"+listS[i]);
File.copy(pathS,pathD);
print(i,"-","Move Nazo Data to : ",nazoDataDir+getTimeStampAlpha()+"_"+listS[i]);
nazoCount++;
}
//exists only S
}else{ //if File don't exists in destination dir
//copy S file to D
File.copy(pathS,pathD);
print(i,"-","Copy New Data to : ",pathD);
newCount++;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Define listFilesDst (loop in dstDir)
function listFilesDst(dirS,dirD){
listD = getFileList(dirD);
for(i=0; i<listD.length; i++){
if(endsWith(listD[i],"/")){
listFilesDst(dirS+listD[i],dirD+listD[i]);
}else{
checkFilesDst(dirS+listD[i],dirD+listD[i]);
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function checkFilesDst(pathS,pathD){
//exists only D
if(!File.exists(pathS)){
//Detect deleted file
print("");
print("File.delete(fail...0, success...1)");
//copy D file to old and delete
File.copy(pathD,oldDataDir+getTimeStampAlpha()+"_"+listD[i]);
File.delete(pathD);
print(i,"-","Move deteted Data to : ",oldDataDir+getTimeStampAlpha()+"_"+listD[i]);
deleteCount++;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function seekDeletedDir(pathS,pathD){
listD = getFileList(pathD);
for(i=0;i<listD.length;i++){
if(endsWith(pathD+listD[i],"/")){
if(File.exists(pathS+listD[i])){
//the object is directory && Exists in srcDir...move a level down
seekDeletedDir(pathS+listD[i],pathD+listD[i]);
}else{
//the object is directory && Not exists in srcDir...detect disappeared directory
deleteDir(pathD+listD[i]);
}
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function deleteDir(dir){
//Up to this line, files in dir must be deleted
list = getFileList(dir);
for(i=0;i<list.length;i++){
if(endsWith(list[i],"/")){
deleteDir(dir+list[i]);
}
}
//when reaching the endpoint of one recursive search,
//it is confirmed that there are no more dir,
print(dir);
File.delete(dir);
//After deletion, performe recursive search at the same level,
//or go back to the parentDir one level up.
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function validateSrc(dirS,dirD){
listS = getFileList(dirS);
for(i=0; i<listS.length; i++){
if(endsWith(listS[i],"/")){
validateSrc(dirS+listS[i],dirD+listS[i]);
}else{
if(File.exists(dirD+listS[i])){
timeS = File.lastModified(dirS+listS[i]);
timeD = File.lastModified(dirD+listS[i]);
if(timeS != timeD){
print(dirS+listS[i]);
flagMissing = 1;
}
}else if(!File.exists(dirD+listS[i])){
print(dirS+listS[i]);
flagMissing = 1;
}
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function validateDst(dirS,dirD){
listD = getFileList(dirD);
for(i=0; i<listD.length; i++){
if(endsWith(listD[i],"/")){
validateDst(dirS+listD[i],dirD+listD[i]);
}else{
if(File.exists(dirS+listD[i])){
timeS = File.lastModified(dirS+listD[i]);
timeD = File.lastModified(dirD+listD[i]);
if(timeS != timeD){
print(dirD+listD[i]);
flagMissing = 1;
}
}else if(!File.exists(dirS+listD[i])){
print(dirD+listD[i]);
flagMissing = 1;
}
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Define function getTimeStampAlpha
function getTimeStampAlpha(){
getDateAndTime(year,month,dayOfWeek,dayOfMonth,hour,minute,second,msec);
timeStamp = "string";
strYear = ""+year;
month = month+1;
if(month < 10){
strMonth = "0"+month;
}else{
strMonth = ""+month;
}
if(dayOfMonth < 10){
strDayOfMonth = "0"+dayOfMonth;
}else{
strDayOfMonth = ""+dayOfMonth;
}
if(hour < 10){
strHour = "0"+hour;
}else{
strHour = ""+hour;
}
if(minute < 10){
strMinute = "0"+minute;
}else{
strMinute = ""+minute;
}
if(second < 10){
strSecond = "0"+second;
}else{
strSecond = ""+second;
}
strMsec = "" + msec;
strMsec = zeroPad(strMsec,3);
timeStamp = strYear+strMonth+strDayOfMonth+"_"+strHour+"h"+strMinute+"m"+strSecond+"s"+strMsec;
return timeStamp;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Define countFiles
function countFiles(dir){
list = getFileList(dir);
for(i=0; i<list.length; i++){
if(endsWith(list[i],"/")){
countFiles(dir+list[i]);
}else{
totalFiles++;
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function emptyDir(dirS,dirD){
listD = getFileList(dirD);
for(i=0; i<listD.length; i++){
if(endsWith(listD[i],"/")){
totalFiles = 0;
countFiles(dirD+listD[i]);
if(totalFiles == 0){
if(!File.exists(dirS+listD[i])){
print(dirD+listD[i]);
flagEmpty = 1;
continue;
}
}
emptyDir(dirS+listD[i],dirD+listD[i]);
}else{
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Define function zeroPadding
function zeroPad(int,digitZeroPad){
if(int < 0){
exit("ZeroPadding Error!! int<0");
}
stringInt = ""+int;
digitStringInt = lengthOf(stringInt);
digitSubtra = digitZeroPad-digitStringInt;
if(digitSubtra < 0){
exit("ZeroPadding Error!! digitSubtra<0");
}
if(digitZeroPad > 0){
for(i=0; i<digitSubtra; i++){
stringInt = "0"+stringInt;
}
}
return stringInt;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//Define function whatTimeNow
function whatTimeNow(){
getDateAndTime(year,month,dayOfWeek,dayOfMonth,hour,minute,second,msec);
stringTime = "string";
strYear = ""+year;
month = month+1;
if(month < 10){
strMonth = "0"+month;
}else{
strMonth = ""+month;
}
if(dayOfMonth < 10){
strDayOfMonth = "0"+dayOfMonth;
}else{
strDayOfMonth = ""+dayOfMonth;
}
if(hour < 10){
strHour = "0"+hour;
}else{
strHour = ""+hour;
}
if(minute < 10){
strMinute = "0"+minute;
}else{
strMinute = ""+minute;
}
if(second < 10){
strSecond = "0"+second;
}else{
strSecond = ""+second;
}
stringTime = strYear+"/"+strMonth+"/"+strDayOfMonth+"_"+strHour+":"+strMinute+":"+strSecond;
return stringTime;
}
//-----------------------------------------------------------------------------
「ストレージA」に保管してあるデータを「ストレージB」にバックアップとして複製する、つまりミラーリングのためのマクロです。
Macでバックアップといえば「TimeMachine」ですが、1台のMacには1台のバックアップストレージという制限があったり、ストレージからストレージへのバックアップが出来なかったりします。
そこで、既製ソフトを使わずに、自由な設定で気楽にバックアップを行えるようにマクロを作成しました。
動作内容
① ストレージAの「Data」ディレクトリの中身を、ストレージBの「BackUp」ディレクトリに、下位のディレクトリも含めてまるまる複製。
② 2回目以降のバックアップでは、ストレージAとBの内容物のファイル名と最終更新日を比較。変化なしのファイル、新しく追加されたファイル、更新されたファイル、消去されたファイルを判別し、正しくミラーリングする。詳細は手順③〜⑤。
③ ストレージA内のファイルが追加or更新されていたら、そのファイルをストレージB内に保存、上書き。その際、ストレージBの更新前ファイルは念のためストレージBの「oldData」ディレクトリに待避。
④ ストレージB内のバックアップファイルのみが更新されていたら、これはあまりいいことではなさそうなので、その更新されたファイルをストレージBの「nazoData」ディレクトリにひとまず待避。ストレージAの更新前ファイルを、ストレージBに上書き。
⑤ ストレージA内のファイルが消去もしくは場所移動されて見当たらない場合、ストレージB内のファイルを消去。消去したデータは念のためストレージBの「oldData」ディレクトリに待避。
⑤ ディレクトリも同様で、ストレージA内のディレクトリが消去もしくは場所移動されていたら、ストレージB内のディレクトリを消去。念のための退避はしない。
⑥ バックアップ作業が正しく行われたかどうかを確認するために、ストレージA、B内のファイル数をカウント。さらにファイル名と最終更新日がA、B間で一致しなかった場合は「エラー」として通知する。
細かな説明
・コピー元のディレクトリ「Data」、コピー先のディレクトリ「BackUp」、古いデータや削除されたデータを保管するディレクトリ「oldData」、なぜかバックアップのみ更新されていたデータを待避させるディレクトリ「nazoData」の四つのディレクトリが必要。あらかじめディレクトリを手動で作成しておき、パスをマクロコード内で指定しておく。oldDataとnazoDataは、ストレージBのBackUpディレクトリの外側に作る。
・パスを指定する際、日本語は使わないようにする。つまりフォルダ名は英数字のみで設定する。日本語をマクロ内に記入した場合、上部タブ[Plugins]から起動すると文字化けによりうまく動作しない。(ただしEditでマクロ入力ウィンドウを開いておいてRun Macroすると普通に動作する)
・ImageJマクロにおけるファイルやディレクトリの消去はFile.deleteだが、ディレクトリ消去の場合はその下位にファイルやディレクトリが存在しないことが条件。そのため、まずファイル消去 → 一番下の階層のディレクトリから順に消去、という順番で処理する。
・ファイルやディレクトリの消去が成功したかどうかはLogウィンドウでわかる。成功なら1、失敗なら0が表示される。使用しているコンピュータによっては、子フォルダを消したあとの親フォルダが消去出来なくなるという謎の現象が起こることもある。当方の環境ではWindows OK、新Mac OK、旧Mac NG、であった。詳細は不明。
・ディレクトリ消去不可エラーに備え、中身が空っぽのディレクトリ(ストレージB内)の存在を調査し、Logウィンドウで通知する。
以上です!
私の場合、自炊データのメイン保管用ストレージαは「TimeMachine」でストレージβに自動バックアップしています。
なんですが、最近ストレージαの容量が満杯になってしまったせいで、第2自炊保管庫をストレージβ内のバックアップ部分とはセパレートされた領域に作成し、第2保管庫のバックアップをそれとはまた違う第3のストレージγにバックアップする、という状況が生まれてしまいました。
この複雑怪奇なバックアップ環境に対応するために、今回紹介したバックアップマクロを使用しています。
自炊データは大切な財産なので、気になった方はぜひこのバックアップマクロを試してみてください!
おしまい