我正在尝试修剪一些我拥有的文件。我将为您节省到目前为止所写的野兽,并通过提供一个虚构的代码来简化它。
让我们看一下这个数组:
[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt'
这时,我需要做三件事:
我的第一个想法是使用一些RegEx:
$Collection[0] -match '^(.+)\.(bronze|silver|gold).txt'
我在这里使用[0]
,因此无需编写foreach就可以对其进行测试。
以上内容将Invitational
存储在$Matches[1]
中,并将Gold
存储在$Matches[2]
中。
现在,我可以使用$Collection
按匹配类型过滤Where-Object
。但是然后我在步骤3中遇到了问题。您看到,如果我使用一个foreach-construct,它将执行match
3次,并获得3次金牌。而且由于它是第一次删除文件,因此在第二次和第三次运行期间会产生错误。
那么有人可以解释我怎样才能对每个比赛类型进行一次查找?换句话说:
Invitational.Gold
Invitational
的奖牌,并找到了已删除的银牌和铜牌。Invitational.Bronze.txt
它看起来是从Invitational
看是否还有其他奖牌,并找到了银牌和金牌。继续尝试再次删除青铜和银,导致错误,因为它们已被删除。这就是我需要避免但一直无法避免的事情。那么我该如何做到这一点,同时又避免了重复的代码块(在每次操作之后重新编写$Collections
内容)?
我能想到的最巧妙的解决方案是以某种方式使用Group-Object为每个匹配类型创建组,然后每个组仅处理一次。但是我不知道该怎么做。
编辑: 我现在正在考虑以下方面:
$Result = $Collection | ForEach-Object -Process {
$Null = $PSItem -match '^(.+)\.(bronze|silver|gold).txt'
$Properties = @{
'MatchType' = $Matches[1]
'Object' = $PSItem
}
New-Object -TypeName PSObject -Property $Properties
}
$Result | Group-Object -Property MatchType
现在我可以进行分组了。我认为。我会耐心等待更好的/其他建议。
答案 0 :(得分:1)
这就是我的想法。但是可能会有一种更快的方法...但是重复此特定的100次总共花了36毫秒...
[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt'
$collectionToOrder = $null
$collectionToOrder = foreach ($item in $Collection) {
$order = $null
if ($item -match "Invitational") {$order = "1" }
if ($item -match "Olympics") {$order = "2" }
if ($item -match "Gold") {$order += "a" }
if ($item -match "Silver") {$order += "b" }
if ($item -match "Bronze") {$order += "c" }
[pscustomobject] @{Order=$order
list=$Item
}
}
$collectionOrdered = $collectionToOrder | sort -property order
Remove-Variable collectionToOrder
$collectionToDelete = ($collectionOrdered | where order -Match "1")[1..5]
$collectionToDelete += ($collectionOrdered | where order -Match "2")[1..5]
foreach ($item in $collectionToDelete) {remove-item $item.list}
可以稍作修改(取决于您想要的输出),但是基本上我使第一组搜索需求添加了一个名为Order的属性,其值为1,2,依此类推...然后添加了下一组到属性a,b,c等。然后您可以继续添加到文本中。这意味着它可能有第三个或第四个理由要添加到订单中。最后按顺序排序问题是,如果它找到不同的匹配词(例如,silver.gold.Olympics),它将时髦地添加到order属性中,但是您的示例并没有将此作为可能的问题。
添加了另一部分以删除错误的文件。请注意,它会从当前工作目录中删除。
您最终可以做些类似的事情
foreach ($item in $collectionToDelete) {remove-item $folder\$item.list}
如果在顶部将$ folder变量设置为您要工作的地方
为时已晚,我重新编写它以使其工作方式有所不同,它不在乎您使用的是什么单词...
[System.String[]]$Collection = 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Invitational.Gold.txt', 'Olympics.Bronze.txt', 'Olympics.Silver.txt'
foreach ($item in $Collection) {
if ($item -match "Silver") {
remove-item $item.replace('.Silver.txt','.Bronze.txt')
}
}
foreach ($item in $Collection) {
if ($item -match "Gold") {
remove-item $item.replace('.Gold.txt','.Silver.txt')
remove-item $item.replace('.Gold.txt','.Bronze.txt')
}
}
这已经越来越长了,哈哈...无论如何,我回答了你,(是哪个天才)并简化了...
[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'
$Collection | ForEach-Object -Process {
If ($PSItem -match 'Silver') {
$name = ($_ -split 'Silver') -join 'Bronze'
If ($Collection -contains $name) { "Removing: $Name" }
} # If 'Silver'
} # ForEach-Object
$Collection | ForEach-Object -Process {
If ($PSItem -match 'Gold') {
$name = ($_ -split 'Gold') -join 'Silver'
If ($Collection -contains $name) { "Removing: $Name" }
$name = ($_ -split 'Gold') -join 'Bronze'
If ($Collection -contains $name) { "Removing: $Name" }
} # If 'Gold'
} # ForEach-Object
答案 1 :(得分:1)
要以特定方式对值进行排序,则需要实现索引的排序。
$MedalValue = @{
Gold = 3;
Silver = 2;
Bronze = 1;
}
[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt'
$DataSet = foreach ($Item in $Collection){
$File = [regex]::Split($Item,'\.')
New-Object PSObject -Property @{
Type = $File[0];
Medal = $File[1];
Value = $MedalValue[$File[1]];
}
}
$DataSet | Sort-Object @{expression='Type';Ascending=$true},@{expression='Value';Descending=$true} | Select-Object Type, Medal
输出:
Type Medal
---- -----
Invitational Gold
Invitational Silver
Invitational Bronze
Olympics Silver
Olympics Bronze
不确定我是否完全理解您的问题。但是,如果您想获得每种类型的头条信息,则可以使用选择和排序,如下所示:
$DataSet |
Group-Object Type |
ForEach-Object {
$Name = $_.Name
$DataSet |
Where-Object {$_.Type -eq $Name} |
Sort-Object -Property Value -Descending |
Select @{Label='Files';Expression={'{0}.{1}.txt' -f $_.Type,$_.Medal}} -First 1
}
输出:
Files
----
Invitational.Gold.txt
Olympics.Silver.txt
答案 2 :(得分:1)
我尝试了一种稍微不同的方法。 [咧嘴]
按“ $ _。Split('。')[0]”分组,检查是否有金/银/青铜,然后使用IF / ELSEIF删除之后的项目。
这可能需要检查“删除”文件或将cmdlet设置为忽略“未找到”错误。
yourfile_2.csv
希望有帮助,
李
答案 3 :(得分:0)
使用给出的三个答案中的每一个之后,我在每种情况下都创建了一个可行的解决方案,试图在尽可能适用于我的情况的同时尽可能地靠近解决方案。这些是我想出的:
@Kirill Pashkov介绍了权重/价值的概念,我发现使用它非常有趣:
[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'
$Weight = @{
'Gold' = 1;
'Silver' = 2;
'Bronze' = 3;
} # Weight
$DataSet = $Collection | ForEach-Object -Process {
$Null = $PSItem -match '^(.+)\.(bronze|silver|gold).txt'
$Properties = @{
'Type' = $Matches[1]
'Medal' = $Matches[2]
'Weight' = $Weight[$Matches[2]]
'Name' = $PSItem
} # Properties
New-Object -TypeName PSObject -Property $Properties
} # ForEach-Object
$DataSet | Group-Object -Property 'Type' | ForEach-Object -Process {
$PSItem.Group | Sort-Object -Property 'Weight' -Descending | Select-Object -SkipLast 1 | ForEach-Object -Process {
Write-Output -InputObject ('Removing: {0}' -f $PSItem.Name)
} # ForEach-Object
} # ForEach-Object
输出:
Removing: Invitational.Bronze.txt
Removing: Invitational.Silver.txt
Removing: Olympics.Bronze.txt
@Lee_Dailey成功地将Group-By
放在了一条直线上。我无法解决这个问题,但是我使用了其他答案将其缩减为三行。然后,我不得不重新构造结果表,以根据不需要的文件的字符串值来过滤它们。而且我必须避免只包含一个文件的组。使用break
,我可以防止来自Switch
语句的重复结果:
[System.String[]]$Collection = @(
'Invitational.Gold.txt'
'Invitational.Bronze.txt'
'Invitational.Silver.txt'
'Olympics.Silver.txt'
'Olympics.Bronze.txt'
'World.Open.Silver.txt'
) # $Collection
$DataSet = $Collection | ForEach-Object -Process {
$Null = $PSItem -match '^(.+)\.(bronze|silver|gold).txt'
[PSCustomObject] @{'Type' = $Matches[1]; 'Object' = $PSItem}
}
$GroupedCollection = $DataSet | Group-Object -Property 'Type'
ForEach ($Grouping in $GroupedCollection) {
If ($Grouping.Count -gt 1) {
$GP_Item = [PSCustomObject]@{'Name' = $Grouping.Name; 'Group' = ($Grouping.Group | Select-Object -ExpandProperty 'Object')}
Switch -Regex ($GP_Item.Group) {
'gold' {
"Keeping: $($GP_Item.Group -match 'gold')"
"Removing: $($GP_Item.Group -match 'silver')"
"Removing: $($GP_Item.Group -match 'bronze')"
Break
} # Gold
'silver' {
"Keeping: $($GP_Item.Group -match 'silver')"
"Removing: $($GP_Item.Group -match 'bronze')"
Break
} # Silver
} # Switch
} # If
} # ForEach
输出:
Keeping: Invitational.Gold.txt
Removing: Invitational.Silver.txt
Removing: Invitational.Bronze.txt
Keeping: Olympics.Silver.txt
Removing: Olympics.Bronze.txt
最后,@ Robert Cotterman使用字符串操作和RegEx获得了一个非凡的解决方案,以获得所需的结果。我不得不重新编写代码以说明匹配类型未知,但是设法通过大量的管道使用使它最终运行:
[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'
[System.Int16]$Count = 1
$MatchTypes = $Collection | ForEach-Object -Process {
$Null = $PSItem -match '^(.+)\.(bronze|silver|gold).txt'
$Matches[1]
} | Select-Object -Unique | ForEach-Object -Process {
[PSCustomObject]@{'Name' = $PSItem; 'Order' = $Count}
$Count += 1
}
$CollectionToOrder = ForEach ($Item in $Collection) {
ForEach ($Type in $MatchTypes) { If ($Item -match $Type.Name) {[System.Int16]$Group = $Type.Order} }
Switch -Regex ($Item) {
'Gold' { $Weight = 'a' }
'Silver' { $Weight = 'b' }
'Bronze' { $Weight = 'c' }
} # Switch
[PSCustomObject]@{ 'Group' = $Group; 'Weight' = $Weight; 'List' = $Item }
} # ForEach
$CollectionOrdered = $CollectionToOrder | Sort-Object -Property 'Group', 'Weight' -Descending
[System.Int16]$TypeCount = ($CollectionOrdered | Select-Object -Property 'Group' -Unique).Count
$CollectionToDelete = 1..$TypeCount | ForEach-Object -Process { $CollectionOrdered | Where-Object -Property 'Group' -Match $PSItem | Select-Object -SkipLast 1 }
$CollectionToDelete | ForEach-Object -Process { Write-Output -InputObject ('Removing: {0}' -f $PSItem.List) }
输出:
Removing: Invitational.Bronze.txt
Removing: Invitational.Silver.txt
Removing: Olympics.Bronze.txt
我的一个烦恼是我无法将我的-matches
行转换成[regex]::match
行,但这是另一个问题。 [PSCustomObject]
与[System.Management.Automation.PSCustomObject]
最后,我可能会选择@Kirill Pashkov的解决方案,因为它似乎是最优雅的解决方案,并且允许我通过CSV来填充Weight变量(尽管我需要操纵RegEx查询也是如此)。因此,除非我的答案被自动选择为可接受的答案,否则我很可能会选择他的答案。
我在周末损失了一半,但是学到了很多东西,现在可以回去做我的文件服务器操作项目了。谢谢大家的帮助!
编辑:
@Robert Cotterman提出了我花了一整天的超简单方法来完成工作的道具。只是表明它不必复杂。我发现split
方法在每个字符的每次出现时都会拆分,但是-split
运算符使用RegEx并可以处理整个单词。考虑到这一点,以及奖牌后可能有 的可能性,我最终还是以奖牌代替了replace
。
[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'
$Collection | ForEach-Object -Process {
If ($PSItem -match 'Silver') {
$Split = $PSItem -split 'Silver'
$Name = ('{0}Bronze{1}' -f $Split[0], $Split[1])
If ($Collection -contains $Name) { "Removing: $Name" }
} # If
} # ForEach-Object
$Collection | ForEach-Object -Process {
If ($PSItem -match 'Gold') {
$Split = $PSItem -split 'Gold'
$Name = ('{0}Silver{1}' -f $Split[0], $Split[1])
If ($Collection -contains $Name) { "Removing: $Name" }
} # If
} # ForEach-Object
输出:
Removing: Invitational.Bronze.txt
Removing: Olympics.Bronze.txt
Removing: Invitational.Silver.txt
@Robert Cotterman的最后编辑帮助我重新考虑了-replace
运算符。在所有三枚奖牌都存在的情况下,还需要使用elseif
来避免在第二ForEach
期间出现重复结果。
[System.String[]]$Collection = 'Invitational.Gold.txt', 'Invitational.Bronze.txt', 'Invitational.Silver.txt', 'Olympics.Silver.txt', 'Olympics.Bronze.txt', 'World.Open.Silver.txt'
$Collection | ForEach-Object -Process {
If ($PSItem -match 'Silver') {
$Bronze = $PSItem -replace ('Silver', 'Bronze')
If ($Collection -contains $Bronze) { "Removing: $Bronze" }
} # If 'Silver'
} # ForEach-Object
$Collection | ForEach-Object -Process {
If ($PSItem -match 'Gold') {
$Silver = $PSItem -replace ('Gold', 'Silver')
$Bronze = $PSItem -replace ('Gold', 'Bronze')
If ($Collection -contains $Silver) { "Removing: $Silver" }
ElseIf ($Collection -contains $Bronze) { "Removing: $Bronze" }
} # If 'Gold'
} # ForEach-Object
输出:
Removing: Invitational.Bronze.txt
Removing: Olympics.Bronze.txt
Removing: Invitational.Silver.txt