Skip to content

Instantly share code, notes, and snippets.

@nyanshiba
Last active December 27, 2018 21:05
Show Gist options
  • Save nyanshiba/d21a1e1affe8ef9cdfb3568eb843a07a to your computer and use it in GitHub Desktop.
Save nyanshiba/d21a1e1affe8ef9cdfb3568eb843a07a to your computer and use it in GitHub Desktop.
#181228
<#
ParentCountMin = 1
ParentInterval = 3
IfParentCountOver = "rendiff"
Gen: 0123456789A...
Parent: PPPPPPR <-Rename
Child: CCCCCX <-Delete
CCCCX <-Delete
| PPPPPPR
Gen CCCCCX
| CCCCX
| PPPP
Gen CCC
| CC
P
#>
[array]$Content += "Start: $((Get-Date).ToString("yyyy/MM/dd (ddd) HH:mm:ss"))"
$Content | Select-Object -Last 1
#====================ユーザ設定====================
#設定用の連想配列
$Settings =
@{
#バックアップに関する設定
Backup =
@{
#世代ディレクトリ用の日付
MyDateTime = (Get-Date).ToString("yyMMdd_HHmmss")
#除外ディレクトリ
ExclPath = 'logs','tsfiles'
#親(Parent)世代の最小数(世代グループ数)(ge 1)
ParentCountMin = 4
#P世代を挟む間隔(1世代グループ内の世代数)(ge 2)
ParentInterval = 5
#古いP世代の扱い(delfull|rendiff)
IfParentCountOver = "rendiff"
}
#ログに関する設定
Log =
@{
#$True:有効 $False:無効
Toggle = $True
#ログ保存ディレクトリ
Path = 'C:\DTV\BackupLog'
#ログローテの閾値
CntMax = 100
}
#通知に関する設定
Post =
@{
#$True:有効 $False:無効
TwitterToggle = $False
#ruby.exeのパス
rubyPath = 'C:\Ruby25-x64\bin\ruby.exe'
#tweetモジュールのパス
ModulePath = 'C:\DTV\EDCB\tweet.rb'
#SSL_CERT_FILEのパス
cert = 'C:\DTV\EDCB\cacert.pem'
#$True:有効 $False:無効
DiscordToggle = $True
#Webhook Url
hookUrl = 'https://discordapp.com/api/webhooks/XXXXXXXXXX'
#$True:有効 $False:無効
BalloonTipToggle = $True
}
#Tempディレクトリ内のファイルの削除
TempDelToggle = $True
}
#バックアップリスト(ハッシュ)
$BackupDirHash =
@(
<#
コピー元 -> コピー先の親ディレクトリ\日付_世代種\コピー先の子ディレクトリ
@{
src = "コピー元";
dstparent = "コピー先の親ディレクトリ";
dst = "コピー先の子ディレクトリ"
},
@{
src = "C:\Users\sbn\Desktop\CDrive\enclog";
dstparent = "C:\Users\sbn\Desktop\DDrive";
dst = "\enclog"
}
#>
@{
src = "C:\DTV";
dstparent = "D:";
dst = "\DTV"
},
@{
src = "C:\Src";
dstparent = "D:";
dst = "\Src";
},
@{
src = "C:\Rec";
dstparent = "D:";
dst = "\Rec";
},
@{
src = "C:\MCServer";
dstparent = "D:";
dst = "\MCServer";
},
@{
src = "C:\Users\sbn\Desktop";
dstparent = "D:";
dst = "\Desktop";
},
@{
src = "C:\Users\sbn\Documents";
dstparent = "D:";
dst = "\Documents";
},
@{
src = "C:\Users\sbn\Pictures";
dstparent = "D:";
dst = "\Pictures";
},
@{
src = "C:\Users\sbn\Music";
dstparent = "D:";
dst = "\Music";
},
@{
src = "C:\Users\sbn\AppData\Roaming\.minecraft";
dstparent = "D:";
dst = "\AppData\Roaming\.minecraft";
},
@{
src = "C:\Users\sbn\AppData\Roaming\Code";
dstparent = "D:";
dst = "\AppData\Roaming\Code";
},
@{
src = "C:\Users\sbn\AppData\Roaming\obs-studio";
dstparent = "D:";
dst = "\AppData\Roaming\obs-studio";
},
@{
src = "C:\Users\sbn\AppData\Roaming\discord\Cache";
dstparent = "D:";
dst = "\AppData\Roaming\discord\Cache";
},
@{
src = "C:\Users\sbn\.ssh";
dstparent = "D:";
dst = "\.ssh";
},
@{
src = "C:\Windows\System32\drivers\etc";
dstparent = "D:";
dst = "\System32\drivers\etc";
},
@{
src = "C:\Windows\System32\Tasks";
dstparent = "D:";
dst = "\System32\Tasks";
}
)
#====================差分バックアップ関数====================
function Backup-Difference
{
param
(
[string]$src,
[string]$dst
)
Write-Output "Robocopy $src -> $dst"
Robocopy $src $dst /E /PURGE /COPY:DAT /XO /NP /XA:SH /R:0 /XD "C:\Rec\mp4" "C:\Rec\ts" "C:\Rec\ShadowPlay" /FFT /BYTES /NFL /NP
}
#====================増分バックアップ関数====================
function Backup-Incremental
{
param
(
[string]$src,
[string]$dst,
[string]$link
)
Write-Output "Copy-Item $src -> $dst (参照:$link)"
$rbcStd = Robocopy $src $link /L /E /NS /FP /XD "C:\Rec\mp4" "C:\Rec\ts" "C:\Rec\ShadowPlay" /FFT
#robocopy /Lのstdを1行ずつループ
$rbcstd | %{
#行に"新しいファイル"が含まれる場合
if ($_ -match "新しいファイル")
{
#ファイルのフルパス(スペース区切り且つスペースを含むパスに対応させている)
$SrcFile = ($_ -split '\s+',3)[2]
#ファイルのフルパス->コピー先\ファイルの相対パス
try
{
Copy-Item -LiteralPath "$SrcFile" "${dst}$($SrcFile.Replace("$src",$null))" -Recurse -ErrorAction SilentlyContinue
} catch
{
#Copy-Itemでディレクトリが存在しない場合の例外
$MakeDir = Split-Path "${dst}$($SrcFile.Replace("$src",$null))" -Parent
$null = New-Item -Path $MakeDir -ItemType Directory -Force
Write-Output "Maked Directory: $MakeDir"
Copy-Item -LiteralPath "$SrcFile" "${dst}$($SrcFile.Replace("$src",$null))" -Recurse -ErrorAction SilentlyContinue
}
}
}
#変数のクリア
$rbcStd = $null
}
#====================NotifyIcon====================
#System.Windows.FormsクラスをPowerShellセッションに追加
Add-Type -AssemblyName System.Windows.Forms
#NotifyIconクラスをインスタンス化
$balloon = New-Object System.Windows.Forms.NotifyIcon
#powershellのアイコンを使用
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon('C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe')
#タスクトレイアイコンのヒントにファイル名を表示
#NotifyIcon.Textが64文字を超えると例外、String.Substringの開始値~終了値が文字数を超えると例外
switch (([string]($MyInvocation.MyCommand.Name) + ":$($Settings.Backup.MyDateTime)").Length)
{
{$_ -ge 64} {$TextLength="63"}
{$_ -lt 64} {$TextLength="$_"}
}
$balloon.Text = ([string]($MyInvocation.MyCommand.Name) + ":$($Settings.Backup.MyDateTime)").SubString(0,$TextLength)
#タスクトレイアイコン表示
$balloon.Visible = $True
#====================Log====================
if ($Settings.Log.Toggle)
{
#ログ取り開始
Start-Transcript -LiteralPath "$($Settings.Log.Path)\$($Settings.Backup.MyDateTime).txt"
#古いログの削除
Get-ChildItem "$($Settings.Log.Path)\*.txt" | Sort-Object LastWriteTime -Descending | Select-Object -Skip $Settings.Log.CntMax | %{
Remove-Item -LiteralPath "$_"
Write-Output "ログ削除:$_"
}
}
#====================TempDel====================
if ($Settings.TempDelToggle)
{
Write-Output "Tempディレクトリ内のファイルを削除"
Remove-Item "$env:temp\*.*" -Recurse -Force
}
#====================ディレクトリ作成・リネーム====================
#配列の展開
$BackupDirHash | foreach {
#自動変数が使えない部分があるため変数に入れとく
$DstParent = $_.dstparent
#親ディレクトリ下のP世代数
$ParentGen = Get-ChildItem -Directory "$DstParent\*_P" -Name -Exclude $Settings.Backup.ExclPath
#世代ディレクトリの存在確認
if (Test-Path "$DstParent\$($Settings.Backup.MyDateTime)_P")
{
#P世代ディレクトリが既にある場合
Backup-Difference -Src "$($_.src)" -Dst "$DstParent\$($Settings.Backup.MyDateTime)_P$($_.dst)"
} elseif (Test-Path "$DstParent\$($Settings.Backup.MyDateTime)_C")
{
#C世代ディレクトリが既にある場合
Backup-Incremental -Src "$($_.src)" -Dst "$DstParent\$($Settings.Backup.MyDateTime)_C$($_.dst)" -Link "$DstParent\$($ParentGen | Select-Object -Last 1)$($_.dst)"
} else
{
#P、Cディレクトリ共に未生成の場合ディレクトリ作成 これらは$BackupDirHash[x].dstparentの設定が変更され、世代グループの状態がちぐはぐになる場合を想定し、毎回計算し直す仕様にしている
#親ディレクトリ下の世代数
$AllGen = Get-ChildItem -Directory "$DstParent\*" -Name -Exclude $Settings.Backup.ExclPath
#$AllGenの最も新しい世代グループ内の世代(C..P)数を$cntに取得
$cnt = 0
foreach ($a in $($AllGen | Sort-Object -Descending))
{
$cnt++
if ($a -match "_P")
{
break
}
}
Write-Output "ParentInterval(Previous Gen): $cnt/$($Settings.Backup.ParentInterval)"
#switch文でも良いがネストが深くなり、自動変数$_が上書きされるのでifで書いてる
if (($cnt -eq 0) -Or ($cnt -ge $Settings.Backup.ParentInterval))
{
#全世代数が0か最新世代グループ内の世代数が$Settings.Backup.ParentInterval = P世代を生成する
Write-Output "New Directory: $DstParent\$($Settings.Backup.MyDateTime)_P"
#現在の世代グループ数と最大世代グループ数設定値を比較
if ($ParentGen.Count -gt $Settings.Backup.ParentCountMin)
{
#$Settings.Backup.ParentCountMinより大きい場合 = 世代グループ数が満杯なので、最も古いPを削除orリネーム、そのグループのCを削除
#P世代の扱い
if ($Settings.Backup.IfParentCountOver -eq 'delfull')
{
#古いP世代を削除&フルバックアップの設定
[array]$Content += "Backup Mode: Parent Generation (Delete Oldest Parent, Child for Full Backup)"
$Content | Select-Object -Last 1
#新しいディレクトリ
$null = New-Item "$DstParent\$($Settings.Backup.MyDateTime)_P" -itemType Directory
#古いPを削除
Remove-Item -LiteralPath "$DstParent\$($ParentGen[0])" -Recurse -Force
Write-Output "Delete: $DstParent\$($ParentGen[0])"
} elseif ($Settings.Backup.IfParentCountOver -eq 'rendiff')
{
#古いP世代をリネーム&差分バックアップの設定
[array]$Content += "Backup Mode: Parent Generation (Rename Oldest Parent, Child for Diff Backup)"
$Content | Select-Object -Last 1
#古いPをリネーム
Rename-Item -LiteralPath "$DstParent\$($ParentGen[0])" "$DstParent\$($Settings.Backup.MyDateTime)_P"
Write-Output "Rename: $DstParent\$($ParentGen[0])"
}
#古いCを削除
Write-Output "残すParent世代数が$($Settings.Backup.ParentCountMin)になるように古い世代を削除します"
#(現在のPの数 - Pを残す数)を飛ばして、残った中で最も古いP世代の名前を参照してループを抜ける
$BreakParentName = $ParentGen | Select-Object -Skip ($ParentGen.Count - $Settings.Backup.ParentCountMin) -First 1
foreach ($a in $($AllGen | Select-Object -Skip 1))
{
if ($a -match "$BreakParentName")
{
#$BreakParentNameと一致したら消さずに抜ける
break
}
Write-Output "Delete: $DstParent\${a}"
Remove-Item -LiteralPath "$DstParent\$a" -Recurse -Force
}
} elseif ($ParentGen.Count -le $Settings.Backup.ParentCountMin)
{
#$Settings.Backup.ParentCountMin以下の場合 = 世代グループ数を増やせるので、新規Pにフルバックアップ
[array]$Content += "Backup Mode: Parent Generation (Full Backup)"
$Content | Select-Object -Last 1
#新しいディレクトリ
$null = New-Item "$DstParent\$($Settings.Backup.MyDateTime)_P" -itemType Directory
}
#差分(または実質フル)バックアップ
Backup-Difference -Src "$($_.src)" -Dst "$DstParent\$($Settings.Backup.MyDateTime)_P$($_.dst)"
} else
{
#最新世代グループ内の世代数が1以上$Settings.Backup.ParentInterval未満 = C世代を生成する
Write-Output "New Directory: $DstParent\$($Settings.Backup.MyDateTime)_C"
[array]$Content += "Backup Mode: Child Generation (Incremental Backup)"
$Content | Select-Object -Last 1
#新しいディレクトリ
$null = New-Item "$DstParent\$($Settings.Backup.MyDateTime)_C" -itemType Directory
#増分バックアップ
Backup-Incremental -Src "$($_.src)" -Dst "$DstParent\$($Settings.Backup.MyDateTime)_C$($_.dst)" -Link "$DstParent\$($ParentGen | Select-Object -Last 1)$($_.dst)"
}
#バックアップディレクトリの世代一覧を取得して文字列に変換
[array]$Content += '```'
[array]$Content += "Backup Generations:"
[array]$Content += "$(Get-ChildItem "$DstParent\*" -Name | Out-String)"
$Content | Select-Object -Last 1
[array]$Content += '```'
}
}
[array]$Content += "End: $((Get-Date).ToString("yyyy/MM/dd (ddd) HH:mm:ss"))"
$Content | Select-Object -Last 1
#====================Post====================
if ($Settings.Post.TwitterToggle -Or $Settings.Post.DiscordToggle -Or $Settings.Post.BalloonTipToggle)
{
#ストレージの使用状況を取得して文字列に変換
#@{Name="SizeRemaining(GB)";Expression={"{0:#,0}" -f ($_.SizeRemaining/1GB)}}
$VolumeInfo = Get-Volume | ?{$_.DriveLetter} | Select @{
Name="Drive";
Expression=
{
$_.DriveLetter
}
},
@{
Name="Enpty(GB)";
Expression=
{
[math]::round($_.SizeRemaining/1GB,0)
}
},
@{
Name="All(GB)";
Expression=
{
[math]::round($_.Size/1GB,0)
}
},
@{
Name="Usage(%)";
Expression=
{
[int]((($_.Size-$_.SizeRemaining)/$_.Size)*100)
}
} | Out-String
[string]$Content = "$($Content | Out-String)"+'```'+"Storage Usage:`n$VolumeInfo`n"+'```'
}
#Twitter
if ($Settings.Post.TwitterToggle)
{
Write-Output "Twitterに通知します"
$env:ssl_cert_file = $Settings.Post.cert
$env:content = $Content
&"$($Settings.Post.rubyPath)" "$($Settings.Post.ModulePath)"
#Start-Process "${ruby_path}" "${tweet_rb_path}" -WindowStyle Hidden -Wait
}
#Discord
if ($Settings.Post.DiscordToggle)
{
Write-Output "Discordに通知します"
$payload = [PSCustomObject]@{
content = $Content
}
$payload = ($payload | ConvertTo-Json)
$payload = [System.Text.Encoding]::UTF8.GetBytes($payload)
Invoke-RestMethod -Uri "$($Settings.Post.hookUrl)" -Method Post -Body $payload
}
#BalloonTip
if ($Settings.Post.BalloonTipToggle)
{
Write-Output "BalloonTipに通知します"
#特定のTipIconのみを使用可
#[System.Windows.Forms.ToolTipIcon] | Get-Member -Static -Type Property
$balloon.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]::Info
#表示するタイトル
$balloon.BalloonTipTitle = "バックアップ終了"
#表示するメッセージ
$balloon.BalloonTipText = $Content
#balloontip_toggle=1なら5000ミリ秒バルーンチップ表示
$balloon.ShowBalloonTip(5000)
#5秒待って
Start-Sleep -Seconds 5
}
#タスクトレイアイコン非表示(異常終了時は実行されずトレイに亡霊が残る仕様)
$balloon.Visible = $False
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment