根据另一个csv文件中的每一行检查csv文件值

时间:2020-04-23 09:32:45

标签: powershell csv

我有两个包含数据的csv文件,我需要检查CSV 2中是否存在CSV 1中的值,如果是,则用file1中的数据替换file2中的该值,如果没有,则跳到另一行,

File1.csv

NO;Description
L001;DREAM
L002;CAR
L003;PHONE
L004;HOUSE
L005;PLANE

File2.csv

ID;Name;Status*;Scheduled Start Date;Actual Start Date;Actual End Date;Scheduled End Date;SLA
144862;DREAM;Scheduled;1524031200;;;1524033000;
149137;CAR;Implementation In Progress;1528588800;;;1548968400;
150564;PHONE;Scheduled;1569456000;;;1569542400;
150564;HOUSE;Scheduled;1569456000;;;1569542400;
150564;PLANE;;;;;;

我尝试了类似的方法,但是它对我不起作用:

    $file1 = Import-Csv "C:\Users\file1.csv" |Select-Object -ExpandProperty Description
$file2 = Import-Csv "C:\Users\file1.csv" |Select-Object -ExpandProperty NO
        Import-Csv "C:\Users\file3.csv" |Where-Object {$file1 -like $_.Name} |ForEach-Object {
          $_.Name = $file2($_.NO)
    } |Out-File "C:\Users\File4.csv"

File4.csv应该这样:

ID;Name;Status*;Scheduled Start Date;Actual Start Date;Actual End Date;Scheduled End Date;SLA
144862;L001;Scheduled;1524031200;;;1524033000;
149137;L002;Implementation In Progress;1528588800;;;1548968400;
150564;L003;Scheduled;1569456000;;;1569542400;
150564;L004;Scheduled;1569456000;;;1569542400;
150564;L005;;;;;;

也许还有另一种方法可以实现我的目标!谢谢

2 个答案:

答案 0 :(得分:0)

这是您可以采用的一种方法:

  • 使用Import-Csv导入两个CSV文件
  • 从第一个CSV文件创建一个查找哈希表,其中要替换的Description是键,而NO是值。
  • 浏览第二个CSV文件,并替换哈希表中Name列中的所有值(如果键存在)。我们可以使用System.Collections.Hashtable.ContainsKey来检查密钥是否存在。这是恒定时间的O(1)操作,因此查找速度很快。
  • 然后,我们可以使用Export-Csv导出最终的CSV。我使用-UseQuotes Never在您的输出文件中没有引号"。此功能仅在 PowerShell 7 中可用。对于较低的PowerShell版本,可以查看How to remove all quotations mark in the csv file using powershell script?,以了解从CSV文件中删除引号的其他替代方法。

演示:

$csvFile1 = Import-Csv -Path .\File1.csv -Delimiter ";"
$csvFile2 = Import-Csv -Path .\File2.csv -Delimiter ";"

$ht = @{}
foreach ($item in $csvFile1) {
    if (-not [string]::IsNullOrEmpty($item.Description)) {
        $ht[$item.Description] = $item.NO
    }
}

& {
    foreach ($line in $csvFile2) {
        if ($ht.ContainsKey($line.Name)) {
            $line.Name = $ht[$line.Name]
        }
        $line
    }
} | Export-Csv -Path File4.csv -Delimiter ";" -NoTypeInformation -UseQuotes Never

我们可以使用Call Operator &,而不是使用Foreach-Objectforeach循环包装在脚本块中。您可以查看about_script_blocks,以获取有关脚本块的更多信息。

$csvFile2 | ForEach-Object {
    if ($ht.ContainsKey($_.Name)) {
        $_.Name = $ht[$_.Name]
    }
    $_
} | Export-Csv -Path File4.csv -Delimiter ";" -NoTypeInformation -UseQuotes Never

File4.csv

ID;Name;Status*;Scheduled Start Date;Actual Start Date;Actual End Date;Scheduled End Date;SLA
144862;L001;Scheduled;1524031200;;;1524033000;
149137;L002;Implementation In Progress;1528588800;;;1548968400;
150564;L003;Scheduled;1569456000;;;1569542400;
150564;L004;Scheduled;1569456000;;;1569542400;
150564;L005;;;;;;

更新

要使用相同的Name处理多个值,我们可以将上面的内容转换为使用System.Management.Automation.PSCustomObject的哈希表,其中我们有两个属性Count来跟踪当前项我们看到的是NO,它是一个数字数组:

$csvFile1 = Import-Csv -Path .\File1.csv -Delimiter ";"
$csvFile2 = Import-Csv -Path .\File2.csv -Delimiter ";"

$ht = @{}
foreach ($row in $csvFile1) {
    if (-not $ht.ContainsKey($row.Description) -and 
        -not [string]::IsNullOrEmpty($item.Description)) {
        $ht[$row.Description] = [PSCustomObject]@{
            Count = 0
            NO = @()
        }
    }
    $ht[$row.Description].NO += $row.NO
}

& {
    foreach ($line in $csvFile2) {
        if ($ht.ContainsKey($line.Name)) {
            $name = $line.Name
            $pos = $ht[$name].Count
            $line.Name = $ht[$name].NO[$pos]
            $ht[$name].Count += 1
        }
        $line
    }
} | Export-Csv -Path File4.csv -Delimiter ";" -NoTypeInformation -UseQuotes Never

答案 1 :(得分:0)

如果文件不太大,则可以通过简单的ForEach-Object循环来实现:

$csv1   = Import-Csv -Path 'D:\Test\File1.csv' -Delimiter ';'
$result = Import-Csv -Path 'D:\Test\File2.csv' -Delimiter ';' | 
          ForEach-Object {
              $name = $_.Name
              $item = $csv1 | Where-Object { $_.Description -eq $name } | Select-Object -First 1
              # update the Name property and output the item
              if ($item) { 
                $_.Name = $item.NO
                # if you output the row here, the result wil NOT contain rows that did not match
                # $_   
              }
              # if on the other hand, you would like to retain the items that didn't match unaltered,
              # then output the current row here
              $_
          }

# output on screen
$result | Format-Table -AutoSize

#output to new CSV file
$result | Export-Csv -Path 'D:\Test\File4.csv' -Delimiter ';' -NoTypeInformation

屏幕结果:

ID     Name Status*                    Scheduled Start Date Actual Start Date Actual End Date Scheduled End Date SLA
--     ---- -------                    -------------------- ----------------- --------------- ------------------ ---
144862 L001 Scheduled                  1524031200                                             1524033000            
149137 L002 Implementation In Progress 1528588800                                             1548968400            
150564 L003 Scheduled                  1569456000                                             1569542400            
150564 L004 Scheduled                  1569456000                                             1569542400            
150564 L005