使用ForEach-Object -PipelineVariable

时间:2016-09-26 11:31:49

标签: powershell csv

我有这种格式的大型CSV文件(简化,更多列)

|Id|Category|Url|

用分号分隔的字段。假设我有一个包含以下数据的文件

id;category;categoryUrl
1;Xyz;http://1.com
2;Xyz;http://1.com
3;Xyz2;http://2.com
4;Xyz;http://2.com

我希望结果是

的结果
id;category;categoryUrl
1;1;2
1;1;2
3;3;4
4;1;4
key;value
1;Xyz
2;http://1.com
3;Xyz2
4;http://2.com

问题的要点类别数据可以是更长的字符串,URL也是如此,实际上有几十列其中一些我想像这样预处理并离开按原样休息。作为一些预处理的一部分,我想用代理ID替换重复值,并将生成的CSV文件写入磁盘(而不是替换原始的)。然后,我还要将生成的代理ID与实际值一起写入另一个CSV文件。

我目前有以下脚本,但出于某种原因,它没有输出任何内容,而且我为何不这样做而傻眼了。有人可以帮忙吗?

$categoryTable = @{}
$categoryId = 0
Import-Csv "data.csv" -Delimiter ';' | ForEach-Object -PipelineVariable row {
  if ($row.category) {
    if (!$categoryTable.ContainsKey($row.category)) {
      $categoryId += 1
      $categoryTable.Add($row.category, $categoryId)
    }
    $category = $categoryTable.Get_Item($row.category)
    $row.category = $category
  }

  if ($row.categoryUrl) {
    if (!$categoryTable.ContainsKey($row.categoryUrl)) {
      $categoryId += 1
      $categoryTable.Add($row.categoryUrl, $categoryId)
    }
    $categoryUrl = $categoryTable.Get_Item($row.categoryUrl)
    $row.categoryUrl = $categoryUrl
  }
} | Export-Csv -Path data_categorized.csv -Force -NoTypeInformation

([PSCustomObject]$categoryTable) | Export-Csv -Path categoryIds.csv -Force -NoTypeInformation

<编辑:

来自 wOxxOm 的提示

Ansgar的解决方案已经完成了!为了其他人的利益,我将完整地包含该脚本

$categoryTable = @{}
Import-Csv "data.csv" -Delimiter ';' -PipelineVariable row | ForEach-Object {
if($row.category) {
    if(-not $categoryTable.ContainsKey($row.category)) {
        $categoryTable[$row.category] = $categoryTable.Count + 1
    }
    $row.category = $categoryTable[$row.category]
}

if($row.categoryUrl) {
    if(-not $categoryTable.ContainsKey($row.categoryUrl)) {
        $categoryTable[$row.categoryUrl] = $categoryTable.Count + 1
    }
    $row.categoryUrl = $categoryTable[$row.categoryUrl]
}

$row
} | Export-Csv -Path categoryIds.csv -Delimiter ';' -Force -NoTypeInformation

$categoryTable.GetEnumerator() | Select-Object @{n='key';e={$_.Value}}, @    {n='value';e={$_.Key}} | Export-Csv -Path categoryIds.csv -Delimiter ';' -Force -NoTypeInformation

1 个答案:

答案 0 :(得分:3)

你得到一个空文件data_categorized.csv,因为你不输出ForEach-Object循环中的行,并且在注释中指出@wOxxOm,由{定义的变量{3}}用于下游cmdlet。将$row替换为-PipelineVariable $_,并在循环结束时添加$_

... | ForEach-Object {
  ...
  $_
} | Export-Csv ...

文件categoryIds.csv应包含数据,但不包含您期望的格式。将哈希表投射到自定义对象将为您提供此输出

1,2,3,4
Xyz,http://1.com,Xyz2,http://2.com

而不是此输出:

key,value
1,Xyz
2,http://1.com
3,Xyz2
4,http://2.com

要获得后者,你需要这样的东西:

$categoryTable.GetEnumerator() | Select-Object Key, Value | Export-Csv ...

此外,您将类别ID定义为哈希表的值,并将类别和类别URL定义为键,因此您实际上可以获得此输出:

key,value
Xyz,1
http://1.com,2
Xyz2,3
http://2.com,4

如果您想要key列中的ID,则需要重新标记字段,例如与current object variable

$categoryTable.GetEnumerator() |
  Select-Object @{n='key';e={$_.Value}}, @{n='value';e={$_.Key}} |
  Export-Csv ...

作为旁注:您的哈希表处理过于复杂。这样的事情就足够了:

if (-not $categoryTable.ContainsKey($row.category)) {
  $categoryTable[$_.category] = $categoryTable.Count+1
}
$_.category = $categoryTable[$_.category]

另请注意,导出不指定自定义分隔符,因此输出文件将以逗号分隔,而不是以分号分隔。将-Delimiter ';'添加到Export-Csv语句中以解决此问题。