Powershell:否定 -like 和 -match 条件的意外行为
Powershell: unexpected behavior of negated -like and -match conditionals
我的 windows 文件夹中有 2 个文件夹,software 和 softwaretest。
所以我有主文件夹 "software" if 语句,然后跳转到 elseif - 这里我有备份文件夹,所以跳转到 else...
我的问题是我正在从 elseif 获取写入主机,并且我有一个备份文件夹,我正在调用 softwaretest,所以不明白为什么它给我那个输出而不是其他的。
希望有人可以 guide/help 我 :-)
If ($SoftwarePathBackup = Get-ChildItem -Path "$Env:SystemRoot" | Where-Object { (!$_.Name -like 'software') }) {
Write-Host ( 'There are no folder named \software\ on this machine - You cant clean/clear/empty the folder!' ) -ForegroundColor Red;
} elseif ($SoftwarePathBackup = Get-ChildItem -Path "$Env:SystemRoot" | Where-Object { ($_.Name -match '.+software$|^software.+') } | Sort-Object) {
Write-Host ( 'There are none folder-backups of \software\ on this machine - You need to make a folder-backup of \software\ before you can clean/clear/empty the folder!' ) -ForegroundColor Red;
} else {
Remove-Item
}
我觉得在右边甚至在 RegEx 中都有否定非常令人困惑。我认为在开始时用 !
或 -not
否定会更明显。
要测试,如果文件夹存在,您可以使用Test-Path
。 Test-Path
还有一个 -Filter
参数,您可以使用它来代替 Where-Object
。但我认为你甚至不必过滤。
$SoftwarePath = "$($Env:SystemRoot)\Software", "$($Env:SystemRoot)\SoftwareBackup"
foreach ($Path in $SoftwarePath) {
if (Test-Path -Path $Path) {
Remove-Item -Path $Path -Force -Verbose
}
else {
Write-Output "$Path not found."
}
}
这对你有用吗?
您的主要问题是运算符优先级之一:
!$_.Name -like 'software'
应该是 ! ($_.Name -like 'software')
或者,最好是
$_.Name -notlike 'software'
- 使用 PowerShell 的 not
前缀运算符进行取反。
同样,您可能想否定 $_.Name -match '.+software$|^software.+'
,这是最容易实现的 $_.Name -notmatch '.+software$|^software.+'
如Get-Help about_Operator_Precedence
所述,!
(a.k.a。-not
)的优先级高于-like
,因此 !$_.Name -like 'software'
被评估为 (!$_.Name) -like 'software'
,这意味着 !$_.Name
的结果 - 布尔值 - 与通配符进行(字符串)比较模式 'software'
,总是 returns $False
,因此永远不会进入 If
分支。
就是说,您可以完全不用 -like
和 -match
并使用 Get-Item
的 -Include
参数支持的隐式通配符匹配(代码段需要 PSv3+ ):
# Get folders whose name either starts with or ends with 'software', including
# just 'software' itself.
$folders = Get-Item -Path $env:SystemRoot\* -Include 'software*', '*software' |
Where-Object PSIsContainer
# See if a folder named exactly 'software' is among the matches.
$haveOriginal = $folders.Name -contains 'software'
# See if there are backup folders among the matches (too).
# Note that [int] $haveOriginal evaluates to 1 if $haveOriginal is $True,
# and to 0 otherwise.
$haveBackups = ($folders.Count - [int] $haveOriginal) -gt 0
# Now act on $folders as desired, based on flags $haveOriginal and $haveBackups.
注意如何使用 Get-Item -Path $env:SystemRoot\*
显式预选 所有 项(添加 -Force
if hidden 项目也应该包括在内),然后通过 -Include
过滤掉。
由于 Get-Item
- 与 Get-ChildItem
不同 - 不支持 -Directory
,| Where-Object PSIsContainer
用于进一步限制对目录的匹配(文件夹).
注:Get-ChildItem
未使用,因为-Include
只对child[=89有效=] (后代)项目(也)当 -Recurse
也被指定时;虽然 -Recurse
可以与 -Depth 0
(PSv3+) 结合使用以限制对直接子目录的匹配,但 Get-ChildItem
显然仍在尝试读取 条目 所有 个子目录,这可能会导致甚至不感兴趣的目录出现不需要的拒绝访问错误。
换句话说:Get-ChildItem -Recurse -Depth 0 -Directory $env:SystemRoot -include 'software*', '*software'
仅当您(至少)具有对 $env:SystemRoot
.
[=88 的 all 子目录的读取权限时才等效=]
我的 windows 文件夹中有 2 个文件夹,software 和 softwaretest。 所以我有主文件夹 "software" if 语句,然后跳转到 elseif - 这里我有备份文件夹,所以跳转到 else...
我的问题是我正在从 elseif 获取写入主机,并且我有一个备份文件夹,我正在调用 softwaretest,所以不明白为什么它给我那个输出而不是其他的。
希望有人可以 guide/help 我 :-)
If ($SoftwarePathBackup = Get-ChildItem -Path "$Env:SystemRoot" | Where-Object { (!$_.Name -like 'software') }) {
Write-Host ( 'There are no folder named \software\ on this machine - You cant clean/clear/empty the folder!' ) -ForegroundColor Red;
} elseif ($SoftwarePathBackup = Get-ChildItem -Path "$Env:SystemRoot" | Where-Object { ($_.Name -match '.+software$|^software.+') } | Sort-Object) {
Write-Host ( 'There are none folder-backups of \software\ on this machine - You need to make a folder-backup of \software\ before you can clean/clear/empty the folder!' ) -ForegroundColor Red;
} else {
Remove-Item
}
我觉得在右边甚至在 RegEx 中都有否定非常令人困惑。我认为在开始时用 !
或 -not
否定会更明显。
要测试,如果文件夹存在,您可以使用Test-Path
。 Test-Path
还有一个 -Filter
参数,您可以使用它来代替 Where-Object
。但我认为你甚至不必过滤。
$SoftwarePath = "$($Env:SystemRoot)\Software", "$($Env:SystemRoot)\SoftwareBackup"
foreach ($Path in $SoftwarePath) {
if (Test-Path -Path $Path) {
Remove-Item -Path $Path -Force -Verbose
}
else {
Write-Output "$Path not found."
}
}
这对你有用吗?
您的主要问题是运算符优先级之一:
!$_.Name -like 'software'
应该是! ($_.Name -like 'software')
或者,最好是
$_.Name -notlike 'software'
- 使用 PowerShell 的not
前缀运算符进行取反。同样,您可能想否定
$_.Name -match '.+software$|^software.+'
,这是最容易实现的$_.Name -notmatch '.+software$|^software.+'
如Get-Help about_Operator_Precedence
所述,!
(a.k.a。-not
)的优先级高于-like
,因此 !$_.Name -like 'software'
被评估为 (!$_.Name) -like 'software'
,这意味着 !$_.Name
的结果 - 布尔值 - 与通配符进行(字符串)比较模式 'software'
,总是 returns $False
,因此永远不会进入 If
分支。
就是说,您可以完全不用 -like
和 -match
并使用 Get-Item
的 -Include
参数支持的隐式通配符匹配(代码段需要 PSv3+ ):
# Get folders whose name either starts with or ends with 'software', including
# just 'software' itself.
$folders = Get-Item -Path $env:SystemRoot\* -Include 'software*', '*software' |
Where-Object PSIsContainer
# See if a folder named exactly 'software' is among the matches.
$haveOriginal = $folders.Name -contains 'software'
# See if there are backup folders among the matches (too).
# Note that [int] $haveOriginal evaluates to 1 if $haveOriginal is $True,
# and to 0 otherwise.
$haveBackups = ($folders.Count - [int] $haveOriginal) -gt 0
# Now act on $folders as desired, based on flags $haveOriginal and $haveBackups.
注意如何使用
Get-Item -Path $env:SystemRoot\*
显式预选 所有 项(添加-Force
if hidden 项目也应该包括在内),然后通过-Include
过滤掉。由于
Get-Item
- 与Get-ChildItem
不同 - 不支持-Directory
,| Where-Object PSIsContainer
用于进一步限制对目录的匹配(文件夹).注:
[=88 的 all 子目录的读取权限时才等效=]Get-ChildItem
未使用,因为-Include
只对child[=89有效=] (后代)项目(也)当-Recurse
也被指定时;虽然-Recurse
可以与-Depth 0
(PSv3+) 结合使用以限制对直接子目录的匹配,但Get-ChildItem
显然仍在尝试读取 条目 所有 个子目录,这可能会导致甚至不感兴趣的目录出现不需要的拒绝访问错误。
换句话说:Get-ChildItem -Recurse -Depth 0 -Directory $env:SystemRoot -include 'software*', '*software'
仅当您(至少)具有对$env:SystemRoot
.