如何使用 PowerShell 对超过 x 天的日志文件启用 NTFS 压缩?

如何使用 PowerShell 对超过 x 天的日志文件启用 NTFS 压缩?

我有一台运行 Windows 2012 R2 的应用程序服务器,它会生成大量日志文件,以至于它会定期耗尽应用程序卷的可用空间。由于应用程序本身的限制,我无法移动或重命名日志文件或启用 NTFS 数据重复数据删除,而且由于这已经不是十年前的事情了,我不想使用批处理或 vbscript 来帮我做这些事情。

日志文件都位于应用程序安装目录的各个子文件夹中,具有不同的扩展名(一个组件将日期添加为日志文件扩展名),并且应用程序安装目录中有一个空格,因为应用程序开发人员是恶意的。写入日志的子文件夹至少专门用于写入日志。这也是一个严重依赖 CPU 的应用程序,所以我不想压缩日志文件夹本身,并承担与为日志写入压缩文件相关的 CPU 惩罚。

如何使用 PowerShell 对超过 x 天的日志文件启用 NTFS 压缩?

答案1

由于 PowerShell 对文件操作的支持仍然相当缺乏,最简单的解决方案是创建一个 PowerShell 脚本来调用该compact.exe实用程序并将其设置为计划任务。由于路径名中有空格,您需要compact.exe直接调用,而不是使用Invoke-WMIMethodCIM_DataFile类(这将导致大量额外的工作来处理路径中的空格)。

假设 X 的年龄为 3 天,您的 PowerShell 脚本将如下所示:

$logfolder="[location of the first logging subfolder]"
$age=(get-date).AddDays(-3)

Get-ChildItem $logfolder | where-object {$_.LastWriteTime -le $age -AND $_.Attributes -notlike "*Compressed*"} | 
ForEach-Object {
compact /C $_.FullName
}

$logfolder="[location of the next logging subfolder]"

Get-ChildItem $logfolder | where-object {$_.LastWriteTime -le $age -AND $_.Attributes -notlike "*Compressed*"} | 
ForEach-Object {
compact /C $_.FullName
}

...

第二个条件是通过跳过已压缩的文件(在第一次运行此脚本后会出现)来加快脚本执行速度。如果您愿意,或者有很多不同的日志子文件夹,那么用重复的 PowerShell 代码创建一个函数可能很有意义,这将是一个相当简单的练习。

答案2

可以使用数组和 foreach 循环来避免重复的代码:

$logfolders=("D:\Folder\One","D:\Folder\Two")
$age=(get-date).AddDays(-3)

foreach ($logfolder in $logfolders) {
    Get-ChildItem $logfolder | where-object {$_.LastWriteTime -le $age -AND $_.Attributes -notlike "*Compressed*"} | 
    ForEach-Object {
    compact /C $_.FullName
    }
}

.....

答案3

无需依赖 compact.exe,即可​​通过直接调用 NTFS 压缩来执行此操作,这是一种“纯 powershell”方法。这还可以处理文件名中的空格以及来自日本的 unicode 文件名,后者很难提供给 compact.exe 命令行。请参阅https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ni-winioctl-fsctl_set_compression也一样。

$MethodDefinition= @'
public static class FileTools
{
  private const int FSCTL_SET_COMPRESSION = 0x9C040;
  private const short COMPRESSION_FORMAT_DEFAULT = 1;
  private const short COMPRESSION_FORMAT_DISABLE = 0;

  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  private static extern int DeviceIoControl(
      IntPtr hDevice,
      int dwIoControlCode,
      ref short lpInBuffer,
      int nInBufferSize,
      IntPtr lpOutBuffer,
      int nOutBufferSize,
      ref int lpBytesReturned,
      IntPtr lpOverlapped);

  public static bool Compact(IntPtr handle)
  {
    int lpBytesReturned = 0;
    short lpInBuffer = COMPRESSION_FORMAT_DEFAULT;

    return DeviceIoControl(handle, FSCTL_SET_COMPRESSION,
        ref lpInBuffer, sizeof(short), IntPtr.Zero, 0,
        ref lpBytesReturned, IntPtr.Zero) != 0;
  }
  public static bool Uncompact(IntPtr handle)
  {
    int lpBytesReturned = 0;
    short lpInBuffer = COMPRESSION_FORMAT_DISABLE;

    return DeviceIoControl(handle, FSCTL_SET_COMPRESSION,
        ref lpInBuffer, sizeof(short), IntPtr.Zero, 0,
        ref lpBytesReturned, IntPtr.Zero) != 0;
  }
}
'@

$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name ‘Kernel32’ -Namespace ‘Win32’ -PassThru

$logfilespec = "c:\Logfolder\*.log"

# compact anything older than three days
foreach ($File in (Get-ChildItem -Path $logfilespec -Recurse -File).Where({$_.LastWriteTime -lt (Get-Date).AddDays(-3) -and $_.Attributes  -notmatch [System.IO.FileAttributes]::Compressed})) {
    $FileObject = [System.IO.File]::Open($File.FullName,'Open','ReadWrite','None')
    $Method = [Win32.Kernel32+FileTools]::Compact($FileObject.Handle)
    $FileObject.Close()
}

# decompact
foreach ($File in (Get-ChildItem -Path $logfilespec -Recurse -File).Where({$_.Attributes  -match [System.IO.FileAttributes]::Compressed})) {
    $FileObject = [System.IO.File]::Open($File.FullName,'Open','ReadWrite','None')
    $Method = [Win32.Kernel32+FileTools]::Uncompact($FileObject.Handle)
    $FileObject.Close()
}

答案4

如果这些日志文件不在 C: 上,请使用 Server 2012 R2 重复数据删除功能。然后,您可以将其配置为仅删除三天前的重复 .log 文件(默认)。第二种方法是控制这种情况,或者当它位于 C: 上时:将日志目录移动到其他驱动器并使用 JUNCTION 指向新位置,最简单的方法是使用 Hardlink-Shell-Extension 从https://schinagl.priv.at/nt/hardlinkshellext/linkshellextension.html- 然后在上面使用 2012 R2 重复数据删除。我发现日志文件和 SQl-dump-for-backup 驱动器的重复数据删除率远高于 90%。

相关内容