喜欢不同 Powershell 版本上的行为

Like behaviour on different Powershell versions

此代码在 Powershell 5+ 中运行良好,但在 Powershell 4.0 和 2.0 中不起作用:

$DaysToDelete = 2

$targets = "profile/Default",
           "profile/Profile 1",
           "profile/Profile 2",
           "profile/Profile 3",
           "profile/Profile 4"

$special = @("chromium", "64")

$profiles = Get-ChildItem "C:\" -Directory -Force |
    Where-Object Name -In $special |
    Select-Object -ExpandProperty FullName

$chromeDir = "C:\Users\*\AppData\Local\Google\Chrome\User Data\Default"
$chromeSetDir = "C:\Users\*\Local Settings\Application Data\Google\Chrome\User Data\Default"

$Items = @("*Archived History*",
            "*Cache*",
            "*Cookies*",
            "*History*",
            "*Top Sites*",
            "*Visited Links*",
            "*Web Data*")

$profiles | ForEach-Object {
    foreach($target in $targets) {
        $profile = Join-Path $_ $target

        $items | ForEach-Object {
        $item = $_ 
        Get-ChildItem $profile, $chromeDir, $chromeSetDir -Recurse -Force -ErrorAction SilentlyContinue |
            Where-Object { ($_.CreationTime -lt $(Get-Date).AddDays(-$DaysToDelete))  -and $_.Directory -like $item} | ForEach-Object { 
            $path = Join-Path $_.DirectoryName $_
            Remove-Item $path -force -Verbose -recurse -ErrorAction SilentlyContinue }
         }

    }
}

我透露中断执行的那一块是

-and $_.Directory -like $item

它在 PS 5+ (Windows 10) 上工作正常,但在 PS 4 (Windows 7) 上找不到任何相似的模式。 Chrome 版本及其目录层次结构在两台机器上相同:59.0.3071.115(官方构建)(64 位).

在版本规范相同的 Win10 上启动脚本

powershell.exe -Version 4.0

什么都没给,运行什么都好。 我对 Powershell 版本细节不太熟悉,因此欢迎专家提出任何建议。 如何使脚本版本独立

更新: Here is 完整代码,但没有提供任何有价值的东西。我验证了所有的地方,并准确地定位了上面的问题行。

另一个有趣的时刻:我发现问题不在 like 子句本身,而是在 combination of like and [=16] =] 检查:

$_.CreationTime -lt $(Get-Date).AddDays(-$DaysToDelete) -and $_.Directory -like $item

如果我单独设置这些条件中的任何一个,一切正常,但如果我将它们组合成单个复合条件,则不会返回任何内容,尽管 个文件夹满足两种情况。

我无法以任何方式解释这一点。

我在评论中提到了这一点;我不确定它是否足够清楚,以及这是否是整个问题。

根据您最初的问题,这条线不起作用。这与 PowerShell 版本无关。

$_.Directory -like $item
  • Get-ChildItem找到文件时,它会return一个System.IO.FileInfo class对象。

    • DirectoryFileInfo class 的 属性,因此文件将具有此 属性.
    • Directory 属于 DirectoryInfo.
    • 类型
  • Get-ChildItem找到folders/directories时,它会return一个System.IO.DirectoryInfo class对象。

    • DirectoryInfo 对象没有 Directory 属性.
    • 他们有 Parent,这也是 return 一个 System.IO.DirectoryInfo 对象
  • 无论哪种情况,您都在处理一个对象并将其与字符串进行比较。实际上,您可能想将文件夹 Name 与字符串进行比较。
    编辑: Windows 8.1 运行 v5.1.14409.1005 为真;预计旧版操作系统也一样。
    Windows 10 运行 PS v5.1.143993.1480 为假;期望在 Server 2016 上相同。不确定 Name/FullName 属性 是如何自动评估的...


Get-ChildItem -File      | Where {$_.Directory      -like "*test*"}   # not ok: comparing object to string.
Get-ChildItem -Directory | Where {$_.Directory.Name -like "*test*"}   # not ok: DirectoryInfo object does not have Directory property

Get-ChildItem -File      | Where {$_.Directory.Name -like "*test*"}   # ok
Get-ChildItem -Directory | Where {$_.Parent.Name    -like "*test*"}   # ok

如何使脚本版本无关?

对我来说,使其独立于版本的最佳方法是使用您需要支持的最低版本,在您的情况下为 2.0。

另一种选择是检测 PowerShell 版本并使您的版本相互独立。

我看了你的剧本, 我建议在实际删除项目之前使用 Test-Path -Path "$var" 工具来测试结果的有效性。

现在开始实际解决您的问题: 我在使用 PowerShell 和清除用户配置文件时遇到过类似的问题。

根据您的具体情况进行调整(绕过 like 的问题):

$newer_file_exist += Get-ChildItem -Path $profile -Recurse -Force -ErrorAction SilentlyContinue | Where-Object {$_.PSIsContainer -eq $FALSE} | where {($_.CreationTime).ToString('yyyy-MM-dd') -lt (get-date).adddays(-$DaysToDelete).ToString('yyyy-MM-dd')};

它将 select 所有比 -$DaysToDelete 更新的文件。它将递归并且 select 仅文件 {$_.PSIsContainer -eq $FALSE}。如果你想 select 目录`{$_.PSIsContainer -eq $TRUE}.

# get all files to be deleted
ForEach ($dir in $profiles_with_path) {
    # to check
    $test_current_pathPath = Test-Path -Path $dir
    If ($test_current_pathPath) {
        #write-host 'Currently writing for these months:'$($time.Name);
        $files_to_delete += Get-ChildItem -Path $dir -recurse -Force | Where-Object {$_.PSIsContainer -eq $FALSE} | % { $_.FullName }
    }
}

删除文件:

  If ($files_to_delete) {
            ForEach ($file in $files_to_delete) { 
                #Remove-Item $file -Recurse -Force -ErrorAction SilentlyContinue
                Remove-Item $file -Force -ErrorAction SilentlyContinue
                If ($? -eq $true) {
                    $files_deleted ++;
                    #Write-Verbose -Verbose "$File deleted successfully!"
                }
            }

所有目录

ForEach ($dir in $profiles_with_path) { #
    Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
    If ($? -eq $true) {
        $directories_deleted ++;
        #Write-Verbose -Verbose "$File deleted successfully!"
    }
}

这些片段适用于 2.0(在 WinXP 和 Windows 2003 (SPx) 和 4.0 Win7 上测试过。

第一次编辑:

我试图为您选择有效代码,但显然我可以做得更好。 逻辑如下:我想删除所有不包含任何超过 3 个月的文件的配置文件。

变量$newer_file_exist - 用于指示是否找到此类文件。如果被发现 整个配置文件被跳过 - 添加到 $excluded_directories.Add($profile)(有关更多信息,请参阅源代码 link)

$profiles_with_path 由 - $profiles_with_path = Get-ChildItem -Path $folder_to_cleanse -exclude $excluded_directories | Where-Object {$_.PSIsContainer -eq $True} 组成 我从 $excluded_directories

中排除了所有目录

这是我在 BB 上制作的 public 脚本: The whole source code on my BB (包括测试逻辑运行、时间规范等)