如何在PowerShell中从数组中移除项?

6

我正在使用 Powershell 1.0 从数组中删除一个元素。以下是我的脚本:

param (
    [string]$backupDir = $(throw "Please supply the directory to housekeep"), 
    [int]$maxAge = 30,
    [switch]$NoRecurse,
    [switch]$KeepDirectories
    )

$days = $maxAge * -1

# do not delete directories with these values in the path
$exclusionList = Get-Content HousekeepBackupsExclusions.txt

if ($NoRecurse)
{
    $filesToDelete = Get-ChildItem $backupDir | where-object {$_.PsIsContainer -ne $true -and $_.LastWriteTime -lt $(Get-Date).AddDays($days)}
}
else
{
    $filesToDelete = Get-ChildItem $backupDir -Recurse | where-object {$_.PsIsContainer -ne $true -and $_.LastWriteTime -lt $(Get-Date).AddDays($days)}
}

foreach ($file in $filesToDelete)
{       
    # remove the file from the deleted list if it's an exclusion
    foreach ($exclusion in $exclusionList)
    {
        "Testing to see if $exclusion is in " + $file.FullName
        if ($file.FullName.Contains($exclusion)) {$filesToDelete.Remove($file); "FOUND ONE!"}
    }
}

我知道在PowerShell中使用Get-ChildItem返回的类型是System.Array。因此,当尝试使用Remove方法时,会出现以下错误:

Method invocation failed because [System.Object[]] doesn't contain a method named 'Remove'.

我想做的是将$filesToDelete转换为ArrayList,然后使用ArrayList.Remove删除项。这个想法好吗?还是应该以某种方式直接操作$filesToDelete作为System.Array?谢谢。
4个回答

9

最好的方法是使用 Where-Object 进行过滤并使用返回的数组。

您也可以使用 @splat 将多个参数传递给命令(在 V2 中新增)。如果您不能升级(如果有可能,应该升级),则只需收集 Get-ChildItems 的输出(仅重复该 CmdLet)并在通用代码中执行所有过滤。

脚本的工作部分如下:

$moreArgs = @{}
if (-not $NoRecurse) {
  $moreArgs["Recurse"] = $true
}

$filesToDelete = Get-ChildItem $BackupDir @moreArgs |
                 where-object {-not $_.PsIsContainer -and 
                               $_.LastWriteTime -lt $(Get-Date).AddDays($days) -and
                              -not $_.FullName.Contains($exclusion)}

在PSH中,数组是不可变的,您不能修改它们,但是创建一个新数组非常容易(像数组上的+=运算符实际上会创建一个新数组并返回它)。

1
是的,我也更喜欢使用 Where-Object。但是在这个问题中有两个循环 - 内部循环遍历 $exclusionList,因此条件应该类似于 -not $($f=$_.Fullname; $exclusionList |?{$f.Contains($_)}) - stej
谢谢Richard - 我能用一个字符串数组来代替$exclusion吗?如果你仔细看代码,你会发现我必须为每个排除调用get-childitem。如果我有很多排除,这样的性能表现不佳。 - Mark Allison
1
@MarkAllison:-exclude 参数接受 string[] 类型,因此您可以传递多个通配符。 - Richard

3
我同意Richard的观点,应该在这里使用Where-Object。然而,它很难阅读。 我的建议是:
# get $filesToDelete and #exclusionList. In V2 use splatting as proposed by Richard.

$res = $filesToDelete | % {
    $file = $_
    $isExcluded = ($exclusionList | % { $file.FullName.Contains($_) } )
    if (!$isExcluded) { 
        $file
    }
}

#the  files are in $res

请注意,通常情况下不可能遍历一个集合并改变它。你会得到一个异常。

$a = New-Object System.Collections.ArrayList
$a.AddRange((1,2,3))
foreach($item in $a) { $a.Add($item*$item) }

An error occurred while enumerating through a collection:
At line:1 char:8
+ foreach <<<< ($item in $a) { $a.Add($item*$item) }
    + CategoryInfo          : InvalidOperation: (System.Collecti...numeratorSimple:ArrayListEnumeratorSimple) [], RuntimeException
    + FullyQualifiedErrorId : BadEnumeration

0

这是古老的代码。但是我之前写过这些代码,使用递归来添加和删除PowerShell列表。它利用了PowerShell进行多重赋值的能力。也就是说,你可以使用$a,$b,$c=@('a','b','c')将a、b和c分配给它们的变量。而使用$a,$b=@('a','b','c')则会将'a'分配给$a,将@('b','c')分配给$b

首先是按项值删除。它将删除第一个出现的项。

function Remove-ItemFromList ($Item,[array]$List(throw"the item $item was not in the list"),[array]$chckd_list=@())
{

 if ($list.length -lt 1 ) { throw "the item $item was not in the list" }

 $check_item,$temp_list=$list
 if ($check_item -eq $item ) 
    {
      $chckd_list+=$temp_list
      return $chckd_list
    }
 else 
    {
     $chckd_list+=$check_item
     return (Remove-ItemFromList -item $item -chckd_list $chckd_list -list $temp_list )
    }
}

这个函数是通过索引来删除元素的。如果在初始调用中传递一个计数值,你可能会把它搞得一团糟。

function Remove-IndexFromList ([int]$Index,[array]$List,[array]$chckd_list=@(),[int]$count=0)
{

 if (($list.length+$count-1) -lt $index )
  { throw "the index is out of range" }
 $check_item,$temp_list=$list
 if ($count -eq $index) 
  {
   $chckd_list+=$temp_list
   return $chckd_list
  }
 else 
  {
   $chckd_list+=$check_item
   return (Remove-IndexFromList -count ($count + 1) -index $index -chckd_list $chckd_list -list $temp_list )
  }
}

0
这是一个非常古老的问题,但问题仍然存在,但没有一个答案适用于我的情况,所以我将提出另一个解决方案。
在我的情况下,我从一个xml配置文件中读取数据,并且我想从数组中删除一个元素。
[xml]$content = get-content $file
$element = $content.PathToArray | Where-Object {$_.name -eq "ElementToRemove" }
$element.ParentNode.RemoveChild($element)

这非常简单且能完成工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接