【问题标题】:Like behaviour on different Powershell versions类似不同 Powershell 版本上的行为
【发布时间】:2017-12-29 15:03:49
【问题描述】:

此代码在 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 (Official Build) (64-bit).

Win10上的启动脚本,版本规范类似

powershell.exe -Version 4.0

什么也没给,它运行得很好。 我对 Powershell 版本的细节不是很流利,所以欢迎高手提出任何建议。 如何使脚本版本独立

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

另一个有趣的时刻:我发现问题不在于like 子句本身,而在于like$_.CreationTime组合 检查:

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

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

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

【问题讨论】:

  • 要检查正在运行的 PowerShell 引擎的版本,check out this post。我怀疑你在 Windows 7 机器上运行 2.0
  • @gms0ulman,不,在控制台中它报告 4 0 -1 -1 就像在那些帖子中一样。 PS 引擎版本能否在终端和在 ISE/任务调度程序中运行脚本时有所不同?
  • 感谢有用的提示。是的,此添加仅适用于第二版。是的,上面的代码在 Win10 中也不适用于-version 2.0
  • 我很难过。 AFAICS 您的代码在 4.05.0 之间不应有所不同; $_.DirectoryFileInfo 的属性。不是特定于 PS 版本的。它不是DirectoryInfo 的一部分; $_.Parent.Name 是要走的路。抱歉,如果这是您已经知道的内容。建议:用更多描述更新您的问题/逐步查看其他地方是否存在问题。注册2.0:不支持-Directory-inWhere的缩写形式

标签: powershell powershell-2.0 powershell-4.0 powershell-5.0


【解决方案1】:

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

根据您最初的问题,此行不起作用。这不依赖于 PowerShell 版本。

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

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

    • DirectoryInfo 对象没有 Directory 属性。
    • 他们有Parent,它也返回一个System.IO.DirectoryInfo对象
  • 在任何一种情况下,您都在处理一个对象并将其与字符串进行比较。当您真的想将文件夹 Name 与字符串进行比较时。
    编辑:适用于运行 v5.1.14409.1005 的 Windows 8.1;对于较旧的操作系统,希望是相同的。
    运行 PS v5.1.143993.1480 的 Windows 10 为 False;期望在 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

【讨论】:

  • 我不能同意。这是我刚才做的测试结果:Win10: works both via $_.Directory.Name and $_.Directory, Win7: does not work in either way.
  • 在您的文件上尝试使用 Pastebin 的完整脚本。
  • @Suncatcher 感谢您的反馈。代码对我来说太多了,很乐意尝试mcve。我在 Win10 上尝试了一些 Cmdlet,可以看出不需要明确引用 .Name 属性。但是仍然没有文件夹的Directory 属性,只有Parent。您是否希望找到文件或文件夹?
  • 我期待两者。但是在这个具体的情况下,它只找到文件,因为它从 Get-ChildItem 获取的文件夹是 C:\chromium\profile\Default\Cache*Cache* 模式匹配,并且这个目录没有子目录。假设它是,我假设它也会找到它们,因为子命令有 -Recurse 指令。
  • @jessehouwing:这仅适用于将-Recurse-Path(隐含的第一个位置参数)结合使用;如果使用-Path 指定目标目录并通过-Filter-Include 指定模式,则过滤会在目标目录的每个级别上进行。层次结构,根据需要。
【解决方案2】:

如何让脚本独立于版本?

对我来说,使其与版本无关的最佳方法是使用您需要支持的最低版本,在您的情况下是 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')};

它将选择所有比-$DaysToDelete 更新的文件。它将递归并仅选择文件{$_.PSIsContainer -eq $FALSE}。如果要选择目录`{$_.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!"
    }
}

这些 sn-ps 适用于 2.0(在 WinXP 和 Windows 2003 (SPx) 上测试,以及在 4.0 Win7 上测试。

第一次编辑:

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

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

$profiles_with_path 由 - $profiles_with_path = Get-ChildItem -Path $folder_to_cleanse -exclude $excluded_directories | Where-Object {$_.PSIsContainer -eq $True} 填充 我从$excluded_directories中排除了所有目录

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

【讨论】:

  • 很难将所有的 sn-ps 放在一起。如何将它们编译成类似我的片段,删除所有早于 X 天的文件和文件夹?我看不到$newer_file_exist var 的使用位置以及$profiles_with_path var 的填充方式
  • @Suncatcher:请参阅编辑。我已经回答了你的问题并提供了完整的源代码。
  • 好吧,你的整个脚本对我来说过于复杂了。我将时间条件应用于($_.CreationTime).ToString('yyyy-MM-dd') -gt (get-date).adddays($time_definition.$($time.Name)).ToString('yyyy-MM-dd'),它似乎不起作用。它只是选择所有文件,而不管我输入$time_definition
  • @Suncatcher:我在离线度假。我认为你没有完全掌握剧本。哈希$time_definition 的定义是@{'3m'="-90"};。它定义了 ForEach 将迭代的时间段 (ForEach($time in time_definition.GetEnumerator())。因此,如果您不想迭代这样的哈希(时间),那么您可以这样做:($_.CreationTime).ToString('yyyy-MM-dd') -gt (get-date).adddays($time).ToString(‌​'yyyy-MM-dd'),例如$time="-90"(您要减去的时间范围(以天为单位)。
  • @Suncatcher 不要忘记你有-gt,而在你的实现中你有-lt
猜你喜欢
  • 2014-05-25
  • 2012-07-09
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-17
  • 1970-01-01
相关资源
最近更新 更多