2016年7月9日土曜日

ローレベル バックアップ.htaを作成した

まだ作成中ですが…最近、HDD内のデータを完全に消去するとか逆に復活するとかいう話がありましたが、今度はデータを維持したいという話がありました。
世の中にはRAIDなど、色々な手段がありますが、利用するには事前に勉強や検証などが必要だったりします。

どうせ手間をかけるのなら、後で自分のやりたいように処理を変えられるやり方で、最低限のことが出来ればとりあえずOKかなと思い、今こんなの作ってます。

※2016/07/12追記:ちょっと色々と忙しくなってしまい、あまり進んでませんが、とりあえずバックアップの方はサブフォルダにも対応しました。テスト機能はまた後日。

※2016/07/20追記:テスト機能も作成しました。自動化するか悩んでます。しばらく使ってみてから必要なら改良します。


<html>
<head>
<title>ローレベル バックアップ</title>
</head>
<body>
データ受け入れ口:<input id=inp受け入れ口 style="width:400px;"><br>
<br>
バックアップ先
<textarea id=ta style="width:100%;height:100px;"></textarea>
<br>
読み込みテスト用:<input id=inp読み込みテスト style="width:400px;"><br>
<br>
<b>[バックアップ]</b><br>
受け入れ口にあるファイルとフォルダのコピーをバックアップ先に作成します。<br>
同名のファイルがバックアップ先に存在する場合は<br>
・サイズが同じの場合…何もしない<br>
・最終更新日が異なる場合…受け入れ口のファイルが新しい場合は、そのファイルでバックアップ先を上書きします。<br>
 バックアップ先ファイルの方が新しい場合は何もせず、動作終了後のレポートで詳細報告します。<br>
・最終更新日が同じでサイズが異なる場合…受け入れ口のファイルでバックアップ先を上書きします。<br>
<br>
<b>[読み込みテスト]</b><br>
バックアップ先のファイルを読み込みテスト用フォルダにコピーできるか確認します。<br>
コピー処理後、ファイルサイズを比較して成否を判定します。<br>
判定が済んだら読み込みテスト用フォルダからはファイルを削除します。<br>
コピー → 判定 → 削除をファイルごとに行います。<br>
<br>
<button id=btnバックアップ>バックアップ実行</button> <button id=btn読み込み>読み込みテスト実行</button><br>
<br>
<div id=div style="border:solid 1px #000;padding:5px;font-family:MS ゴシック;"></div>
</body>
<script>
inp受け入れ口.value = 'C:/app/新しいフォルダー/受け'
ta.value = [
'C:/app/新しいフォルダー/backup0',
'C:/app/新しいフォルダー/backup1'
].join('\n')
inp読み込みテスト.value = 'C:/app/新しいフォルダー/テスト'
txt2arrDAP=function(txt){
// 複数行のテキストを改行で区切って、各行のPath文字列をgetDirAbsPathの返り値にして 配列型で返す。
var arr = txt.split('\r\n')
for(var i=0,L=arr.length,arr出力=[];i<L;i++){
// 空白行は無視する
if(!arr[i]){continue}
// Pathが存在するかどうかは確認しない。
arr出力.push( getDirAbsPath(arr[i]) )
}
return arr出力
}
btnバックアップ.onclick=function(){
// 多重実行できないようにボタンを無効化する。
btnバックアップ.disabled = btn読み込み.disabled = true
var 受け, 先
div[iT] = [
'[受け入れ口]',
受け=getDirAbsPath(inp受け入れ口.value),
'',
'[バックアップ先]',
先 =ta.value,
'',
'[開始日時]',
get日時(),
''
].join('\r\n')
var txt先=gsf2+'\\'+fs.GetTempName(), path
Write(txt先, txt2arrDAP(先).join('\r\n'))
var fun=function(){
arg=WScript.Arguments, 受け=arg(0), txt先=arg(1), pathOut=arg(2), 先=RAS(txt先), len先=先.length
msg異常 = ''
ファイル数 = 0
arrファイル = []
// まずは、1階層分の処理(ファイルの列挙、判定、コピー)を定義。
var カレントファイル, ファイル名
var fun0=function(pathFile){
カレントファイル = pathFile
ファイル名 = fs.GetFileName(カレントファイル)
arrファイル[ファイル数++] = カレントファイル
バックアップ先を列挙する(fun1_Check)
}
var fun1_Check=function(path先){
var path転送後=path先+pathフォルダ終端側+ファイル名
if(!fs.FileExists(path転送後)){ return fun2_Copy(path転送後) }
var file受け=fs.GetFile(カレントファイル), size受け=file受け.Size
var file後 =fs.GetFile(path転送後 ), size後 =file後 .Size
// サイズが同じなら無視
if(size受け==size後){return}
var lastM受け=file受け.DateLastModified, lastM後=file後.DateLastModified
// 最終更新日が異なる場合
if(lastM後 < lastM受け){
// 受けの方が新しいならバックアップ先を上書きする。
return fun2_Copy(path転送後, true)
}
msg異常+=[
'受け入れ口file:'+カレントファイル,
'サイズ :'+size受け,
'最終更新日 :'+get日時(new Date(lastM受け)),
'',
'バックアップ先:'+path転送後,
'サイズ :'+size後,
'最終更新日 :'+get日時(new Date(lastM後)),
'',
''
].join('\r\n')
}
var fun2_Copy=function(path先, swOverWrite){ fs.CopyFile(カレントファイル, path先, swOverWrite) }
// つぎに、多階層に対応するための処理を定義。
var pathフォルダ終端側='', len受け=受け.length+1
var fun0_Dir=function(pathDir){
pathフォルダ終端側 = pathDir.slice(len受け, pathDir.length)+'\\'
バックアップ先を列挙する(fun1_Check_Dir)
fun_Dir(pathDir)
}
var fun1_Check_Dir=function(path先){
var newDir=path先+pathフォルダ終端側
if(!fs.FolderExists(newDir)){ fs.CreateFolder(newDir) }
}
var fun_Dir=function(pathDir){
// 1つのフォルダのPathを受け取って、ファイルの列挙~処理とフォルダの列挙~処理をコールする。
// サブフォルダがあればその数だけコールされる。
ファイルを列挙する(pathDir, fun0)
フォルダを列挙する(pathDir, fun0_Dir)
}
fun_Dir(受け)
報告 = [
msg異常 ? ('異常がありました\r\n'+msg異常) : '異常はありませんでした',
'',
'対象ファイル数:'+ファイル数,
arrファイル.join('\r\n')
].join('\r\n')
Write(pathOut, 報告)
}
var WSF=[
'fun='+fun,
'fun()'
].join('\r\n')
var pathOut = gsf2+'/'+fs.GetTempName(), pathWSF=makeWSF(0, WSF)
execWSF([pathWSF, 受け, txt先, pathOut].join(' '), 1)
var 間隔=1000
setTimeout(function(){
while(true){
if(fs.FileExists(pathOut)){ break }
try{
// 追記モードでのOpenに成功するということは、WSFでの書き込みが終了しているということ。
var ws=fs.OpenTextFile(pathOut,8)
ws.Close()
}catch(e){}
// pathOutが無いか、追記モードでのOpenに失敗した場合は指定時間待機後、リトライする。
return setTimeout(arguments.callee, 間隔)
}
div[iT] += [
'',
'',
'[終了日時]',
get日時(),
'',
'[バックアップ結果]',
Read(pathOut)
].join('\r\n')
btnバックアップ.disabled = btn読み込み.disabled = false
fs.DeleteFile(pathWSF)
fs.DeleteFile(pathOut)
},間隔)
}
btn読み込み.onclick=function(){
var テスト=getDirAbsPath(inp読み込みテスト.value), 先=ta.value
div[iT] = [
'[バックアップ先]',
先,
'',
'[読み込みテスト用]',
テスト,
'',
'[開始日時]',
get日時(),
''
].join('\r\n')
var txt先=gsf2+'\\'+fs.GetTempName(), path
Write(txt先, txt2arrDAP(先).join('\r\n'))
var fun=function(){
arg=WScript.Arguments, テスト=arg(0), txt先=arg(1), pathOut=arg(2), 先=RAS(txt先), len先=先.length
msg異常 = ''
ファイル数 = 0
arrファイル = []
var fun0=function(path先){
ファイルを列挙する(path先, fun1)
フォルダを列挙する(path先, fun0)
}
var fun1=function(pathFile){
var sw異常
try{
fs.CopyFile(pathFile, テスト)
}
catch(e){
// サイズの比較をする前に、コピー自体がエラーになってしまう場合もあるかもしれない
sw異常 = true
msg異常 += [
'バックアップ先:'+pathFile,
'サイズ :'+size先,
'',
'テスト側 :'+pathテスト,
'サイズ :コピーエラー',
'',
''
].join('\r\n')
}
var pathテスト=テスト+fs.GetFileName(pathFile), fileテスト=fs.GetFile(pathテスト), sizeテスト=fileテスト.Size, size先=fs.GetFile(pathFile).Size
if(sizeテスト != size先){
msg異常 += [
'バックアップ先:'+pathFile,
'サイズ :'+size先,
'',
'テスト側 :'+pathテスト,
'サイズ :'+sizeテスト,
'',
''
].join('\r\n')
}
fs.DeleteFile(pathテスト)
}
バックアップ先を列挙する(fun0)
報告 = [
msg異常 ? ('異常がありました\r\n'+msg異常) : '異常はありませんでした',
'',
'対象ファイル数:'+ファイル数,
arrファイル.join('\r\n')
].join('\r\n')
Write(pathOut, 報告)
}
var WSF=[
'fun='+fun,
'fun()'
].join('\r\n')
var pathOut = gsf2+'/'+fs.GetTempName(), pathWSF=makeWSF(0, WSF)
execWSF([pathWSF, テスト, txt先, pathOut].join(' '), 1)
var 間隔=1000
setTimeout(function(){
while(true){
if(fs.FileExists(pathOut)){ break }
try{
// 追記モードでのOpenに成功するということは、WSFでの書き込みが終了しているということ。
var ws=fs.OpenTextFile(pathOut,8)
ws.Close()
}catch(e){}
// pathOutが無いか、追記モードでのOpenに失敗した場合は指定時間待機後、リトライする。
return setTimeout(arguments.callee, 間隔)
}
div[iT] += [
'',
'',
'[終了日時]',
get日時(),
'',
'[読み取りテスト結果]',
Read(pathOut)
].join('\r\n')
btnバックアップ.disabled = btn読み込み.disabled = false
fs.DeleteFile(pathWSF)
fs.DeleteFile(pathOut)
},間隔)
}
// 以下、定型文
iT='innerText'
function makeWSF(path, source){
path = path || (gsf2+'/'+fs.getTempName()+'.wsf')
Write(path, '<'+'job><'+'script>\r\n'+WSFと共用+'\r\nWSFと共用()\r\n\r\n'+source+'\r\n<'+'/script><'+'/job>')
return path
}
function execWSF(path,sw表示,sw同期){
shell.run('cmd /C wscript '+path,sw表示,sw同期)
}
function WSFと共用(){
fs = new ActiveXObject('Scripting.FileSystemObject')
shell = new ActiveXObject('WScript.Shell')
Write = function(path,str){with(fs.CreateTextFile(path)){Write(str);Close()}}
Read = function(path, swCreate, swUnicode){
if(!fs.FileExists(path) && !swCreate){return ''}
var rs=fs.OpenTextFile(path,1,swCreate,swUnicode), str=rs.AtEndOfStream?'':rs.ReadAll()
rs.Close()
return str
}
RAS = function(path){return Read(path).split('\r\n')}
// dirコマンドによるファイル(フォルダ)リスト取得
;(function(){
var funCore=function(path, swDir){
var pathTMP=gsf2+'/'+fs.getTempName()+'.txt'
// dirコマンドが完了するまで待機する。
shell.run('cmd /C dir /A'+(swDir?'':'-')+'D /B "'+path+'" > "'+pathTMP+'"', 0, true)
var arr=RAS(pathTMP)
// 最下行は中身無いので捨てる
arr.pop()
// 後始末
fs.deleteFile(pathTMP)
return arr
}
getFileList=function(path){ return funCore(path) }
getDirList =function(path){ return funCore(path,true) }
})();
gsf2 = fs.GetSpecialFolder(2)
get日時=function(dat){
dat=dat || (new Date())
var 二桁=function(s){s='0'+s; return s.slice(s.length-2, s.length)}
return [dat.getFullYear(), 二桁(dat.getMonth()+1), 二桁(dat.getDate())].join('/')+' '+[二桁(dat.getHours()), 二桁(dat.getMinutes()), 二桁(dat.getSeconds())].join(':')
}
;(function(){
// Pathの末尾が必ず「\\」になるようにする。
var reg=/[\\\/]$/, t='', f='\\'
getDirAbsPath=function(path){return path + (path.match(reg) ? t : f)}
})();
フォルダを列挙する=function(pathDir, func){
var arr=getDirList(pathDir)
for(var i=0,L=arr.length;i<L;i++){ func(pathDir+'/'+arr[i]) }
}
ファイルを列挙する=function(pathDir, func){
var arr=getFileList(pathDir)
for(var i=0,L=arr.length;i<L;i++){ func(pathDir+'/'+arr[i]) }
}
バックアップ先を列挙する=function(func){
for(var i=0;i<len先;i++){ func(先[i]) }
}
}
WSFと共用()
</script>
</html>

とりあえず受け入れ口にあるファイルをバックアップ先にコピーするところまでは出来ました。
あとはサブフォルダに対応するとか、読み取りテストの機能をつけるとか・・。
来週やります。

0 件のコメント:

コメントを投稿