PowerShell で古いファイルを削除する (ログファイルのローテーション)

# ログファイルをパージする

ちょっと検索してみると再帰的に削除するのが見つかったけども、再帰的に削除する必要はそんなにないかな？と思ったので、無しで書いてみる。

```powershell:porge.ps1
# 6か月以上前のファイルを削除
$filePath = 'C:/Program Files (x86)/nxlog/data'

# 圧縮対象のログファイル名 (正規表現)
$logFile = '\d+.log$'
# 削除対象のファイル名は適宜書き換えませう。 (正規表現)
$archivedFile = '\d+.log..bz2$'

# bzip2
# https://github.com/philr/bzip2-windows/releases
#
# VC 2015 ランタイム
# https://www.microsoft.com/ja-jp/download/details.aspx?id=48145
$archiver = 'C:/Program Files (x86)/nxlog/cmd/bzip2.exe'

# ダミーファイルを作る
function make_dummy_log {
	Push-Location -Path $filePath
	0..30 | ForEach-Object {
		$dummy_file = "{0:D4}Days-Ago_00.log" -f $_
		echo "" > $dummy_file
		Set-ItemProperty $dummy_file -Name LastWriteTime -Value $((Get-Date -Hour 0 -Minut 0 -Second 0 -Millisecond 0).AddDays(-$_))
	}
	Pop-Location
}

# 3日以上書き換えられていないファイルは圧縮
function compresses_log {
    Get-ChildItem -Path $filePath -Recurse |`
        where{($_ -match $logFile) -and  ($_.LastWriteTime -lt (Get-Date).AddDays(-3))} |`
        ForEach-Object -Process {
            # タイムスタンプを保存して圧縮
            $lastWriteTime = $_.LastWriteTime
            & $archiver "-q9" $_.FullName
            Set-ItemProperty $("{0}.bz2" -f $_.FullName) -Name LastWriteTime -Value $lastWriteTime
        }
}

# 6日間を超えるファイルは削除
function purge_log {
    Get-ChildItem -Path $filePath -Recurse |`
       Where-Object{($_ -match $archivedFile) -and  ($_.LastWriteTime -lt (Get-Date).AddDays(-6))} | rm -WhatIf

    # 6か月で削除
    # Get-ChildItem -Path $filePath -Recurse -Filter $archivedFile |`
    #     Where-Object{$_.LastWriteTime -lt (Get-Date).AddMonths(-6)} | Remove-Item -Force -WhatIf

    # 再帰的に行わない -Recurse を使わない方法
    # Push-Location -Path $filePath
    # dir $archivedFile  | where{$_.LastWriteTime -lt (Get-Date).AddMonths(-6)} | rm -WhatIf
    # Pop-Location
}

# ダミーファイルが必要な時にコメントアウト
make_dummy_log

# 記録が終わったファイルを圧縮
compresses_log

# 古いファイルを削除
purge_log
```
 -WhatIf がついてるコマンドは実際の削除は行いません。

# タイムスタンプが古いファイルを作成する
make_dummy_log は、古いログファイルっぽいのを作る関数です。

```powershell:
PS C:\Users\Administrator> 1..30 | ForEach-Object {
>> $filename = "{0:D4}.txt.xz" -f $_
>> echo "" > $filename
>> Set-ItemProperty $filename -Name LastWriteTime -Value $((Get-Date).AddDays(-$_))
>> }
PS C:\Users\Administrator> dir *.txt.xz


    ディレクトリ: C:\Users\Administrator


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2021/06/26     10:05              6 0001.txt.xz
-a----       2021/06/25     10:05              6 0002.txt.xz
-a----       2021/06/24     10:05              6 0003.txt.xz
-a----       2021/06/23     10:05              6 0004.txt.xz
-a----       2021/06/22     10:05              6 0005.txt.xz
-a----       2021/06/21     10:05              6 0006.txt.xz
-a----       2021/06/20     10:05              6 0007.txt.xz
-a----       2021/06/19     10:05              6 0008.txt.xz
-a----       2021/06/18     10:05              6 0009.txt.xz
-a----       2021/06/17     10:05              6 0010.txt.xz
-a----       2021/06/16     10:05              6 0011.txt.xz
-a----       2021/06/15     10:05              6 0012.txt.xz
-a----       2021/06/14     10:05              6 0013.txt.xz
-a----       2021/06/13     10:05              6 0014.txt.xz
-a----       2021/06/12     10:05              6 0015.txt.xz
-a----       2021/06/11     10:05              6 0016.txt.xz
-a----       2021/06/10     10:05              6 0017.txt.xz
-a----       2021/06/09     10:05              6 0018.txt.xz
-a----       2021/06/08     10:05              6 0019.txt.xz
-a----       2021/06/07     10:05              6 0020.txt.xz
-a----       2021/06/06     10:05              6 0021.txt.xz
-a----       2021/06/05     10:05              6 0022.txt.xz
-a----       2021/06/04     10:05              6 0023.txt.xz
-a----       2021/06/03     10:05              6 0024.txt.xz
-a----       2021/06/02     10:05              6 0025.txt.xz
-a----       2021/06/01     10:05              6 0026.txt.xz
-a----       2021/05/31     10:05              6 0027.txt.xz
-a----       2021/05/30     10:05              6 0028.txt.xz
-a----       2021/05/29     10:05              6 0029.txt.xz
-a----       2021/05/28     10:05              6 0030.txt.xz
```

この関数を使って思う存分削除のテストをしてください。


### bash スクリプトで同じことをする (for Linux)
800台ぐらいのログのローテーションをしないといけなくなったので bash でも書いてみた。
殆どはダミーファイルを作成するスクリプトですが。

```bash:
#!/usr/bin/bash

ionice -c 3 -p $$  2>&1 > /dev/null
renice -n 20 -p $$ 2>&1 > /dev/null

# ログファイルは /var/log/YYYY-MM/IPaddr-YYYY-MM-DD.log とする
#  →$LOG_BASE/$LOG_SUBDIR/
# local -r LOG_BASE=/var/log
LOG_BASE=$HOME/script
LOG_SUBDIR='[0-9]{4}-[0-9]{2}'

LOG_FILE='*[0-9][0-9].log'
ZIPED_FILE='*[0-9][0-9].log.bz2'

# ログを保持する日数
EXPIRE=200

FIND=/usr/bin/find
XARGS=/usr/bin/xargs
DATE=/usr/bin/date
MKDIR=/usr/bin/mkdir
RMDIR=/usr/bin/rmdir
TOUCH=/usr/bin/touch

cd $LOG_BASE

####
#### テスト用のダミーファイルを作成 ここから
####
# ダミーログの作成期間
START_DATE="2021-03-01"
END_DATE="2021-11-14"

function mk_file(){
	local IP TARGET1 TARGET2 TARGET13
	IP=$(($RANDOM % 255)).$(($RANDOM % 255)).$(($RANDOM % 255)).$(($RANDOM % 255))
	TARGET1="$1/$IP-$2.log"

	IP=$(($RANDOM % 255)).$(($RANDOM % 255)).$(($RANDOM % 255)).$(($RANDOM % 255))
	TARGET2="$1/$IP-$2.log"

	IP=$(($RANDOM % 255)).$(($RANDOM % 255)).$(($RANDOM % 255)).$(($RANDOM % 255))
	TARGET3="$1/$IP-$2.log"

	$TOUCH -d $2 $TARGET1 $TARGET2 $TARGET3
	# 作成したファイルを表示
	/usr/bin/ls -l $TARGET1 $TARGET2 $TARGET3
}

function mk_dummy_logfiles(){
	local END_DATE_tm DO_DATE DIR_YYMM
	END_DATE_tm=$($DATE --date="$END_DATE"   +%s)

	DO_DATE=$(    $DATE --date="$START_DATE" +%F)
	while
		DIR_YYMM=$($DATE --date="$DO_DATE" +%Y-%m)
		$MKDIR -p $DIR_YYMM 
		mk_file $DIR_YYMM $DO_DATE

		# 日付を１日進める
		[ $($DATE --date="$DO_DATE 1days" +%s) -lt $END_DATE_tm ] &&
			DO_DATE=$($DATE --date="$DO_DATE 1days" +%F)
	do
	:
	done
}
mk_dummy_logfiles

####
#### テスト用のダミーファイルを作成 ここまで
####


# ログディレクトリ一覧を取得
function logdir(){
	$FIND "$LOG_BASE" -regextype posix-egrep -regex ".*/$LOG_SUBDIR" -type d
}
LOG_DIRS=`logdir`

# ３日以上前のファイルを圧縮
$FIND $LOG_DIRS -type f -mtime +2 -name $LOG_FILE | $XARGS /usr/bin/bzip2 || true

# 古いファイルを削除
$FIND $LOG_DIRS -type f -mtime +$EXPIRE -name $ZIPED_FILE | $XARGS /usr/bin/rm -f
$RMDIR --ignore-fail-on-non-empty $LOG_DIRS 

```

ダミーログファイルを作成するときに、time_t へ変換するのは START_DATE, END_DATE が変だった場合に無限ループに陥らないため。

# 日付毎にフォルダが分かれている場合 (7zip で、一ヶ月まとめて圧縮)
7zip で、一ヶ月毎に圧縮するスクリプトにしてみた。
タイムスタンプは関係なくなったけど、前回の名残が。

```powershell:purge.ps1
# 6か月以上前のファイルを削除
#
# ログファイルの場所
# D:/log/YYYY-MM/DD/～YYYY-MM-DD.log
$filePath = 'D:/log'

# 7zip
# https://7-zip.opensource.jp/
$archiver = 'D:/Program Files/7-Zip/7z.exe'

# ダミーファイルを作る
function make_dummy_log {
    0..300 | ForEach-Object {
        $dummy_folder = Get-Date (Get-Date).AddDays(-$_) -Format "yyyy-MM/dd/"
        $dummy_file   = Get-Date (Get-Date).AddDays(-$_) -Format "yyyy-MM/dd/AAABBBCCC-yyyy-MM-dd.lo\g"
	mkdir -Force $dummy_folder 2>&1 > $null
        $random_bin = new-object byte[] (Get-Random -Maximum 1048576); (new-object Random).NextBytes($random_bin); [IO.File]::WriteAllBytes( $filePath + "/" + $dummy_file, $random_bin)
        Set-ItemProperty $dummy_file -Name LastWriteTime -Value $((Get-Date -Hour 0 -Minut 0 -Second 0 -Millisecond 0).AddDays(-$_))
    }
}

# 4ヶ月以上前のフォルダは圧縮 (フォルダの中の物は問答無用で圧縮、削除)
function compresses_log {
    12..4 | ForEach-Object {
        $dir = Get-Date (Get-Date).AddMonths(-$_) -Format "yyyy-MM"
        $archived = $dir + ".7z"
        if(Test-Path $dir) {
            $arg = "a -mx=9 -mmt=on -sdel", $archived, $dir
            Start-Process $archiver -ArgumentList $Arg -NoNewWindow -Wait
        }
    }
}

# 7ヶ月以上前のファイルは削除
function purge_log {
    12..7 | ForEach-Object {
        $archived = Get-Date (Get-Date).AddMonths(-$_) -Format "yyyy-MM.7\z"
        if(Test-Path $archived) {
            Remove-Item –Path $archived
        }
    }
}

cd $filePath

# ダミーファイルが必要な時にコメントアウト
# make_dummy_log

# 記録が終わったファイルを圧縮
compresses_log

# 古いファイルを削除
purge_log
```