喜欢不同 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对象。
Directory
是 FileInfo
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
(包括测试逻辑运行、时间规范等)
此代码在 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对象。Directory
是FileInfo
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 运行 PSv5.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 (包括测试逻辑运行、时间规范等)