有没有一种高效的方法可以将CSV用作PowerShell的查找表?

kjthegm6  于 2023-04-03  发布在  Shell
关注(0)|答案(2)|浏览(126)

环境是Windows Server 2016.所以我有一个文件夹与290 k子文件夹.这些子文件夹需要移动.我也有一个CSV文件有两个字段,一个是(数字)文件夹名称,另一个是目标.问题:CSV包含额外的行,不仅是我需要的290 k,而且还有大约530万行。
CSV的结构基本上是:

"ORDER","CLIENT"
11223344,1111
22334455,1111
33445566,2222
44556677,1390

为了澄清,每个文件夹名称都是一个订单。订单文件夹应被分类到客户文件夹。订单是唯一的。不是每个订单都有一个文件夹。
我的第一个方法是加载CSV,然后foreach通过它。这是完全缓慢的,因为我必须检查每一个条目,如果它实际上是一个现有的文件夹。

$rootfolder = path-to-folders
$csvpath = path-to-csv

$csv = Import-csv -path $csvpath

foreach($row in $csv)
{ 
    $path = $rootfolder + $row.ORDER
    $target = $row.CLIENT
    if (Test-Path -Path $path) {
        # copy files to $rootfolder + $target
    }
}

第二种方法是先得到子文件夹的列表,遍历它,然后从CSV中查找对应的值where-object,结果更慢。

$rootfolder = path-to-folders
$csvpath = path-to-csv

$folders = Get-ChildItem -path $rootfolder
$csv = Import-csv -path $csvpath

foreach ($folder in $folders) {
    $currentpath = $rootfolder + $folder
    if (Test-Path -Path $currentpath -PathType Container) {
        $target = $csv | Where-Object ORDER -eq $folder
        # copy files to $rootfolder + $target
    }
}

我的下一个想法是将CSV加载到一个临时数据库中,并从那里进行查找,因为它应该(?)更快。
但也许有更详细的方法相比,我的穴居人的方法?感谢阅读。

kqqjbcuj

kqqjbcuj1#

根据评论我尝试了以下

$reader = [System.IO.File]::OpenText($csvpath)
while($null -ne ($line = $reader.ReadLine().split(','))) {
    $currentpath = $rootfolder + $line[0]
    if (Test-Path -Path $currentpath -PathType Container) {
        # Copy $currentpath to $line[1]
    }
}

这是更快的,它处理像300-400文件夹每秒。

Start: 03/29/2023 10:33:31
End: 03/29/2023 10:48:44

感谢Jeroen Mostert指出了在处理CSV之前阅读CSV是最大的罪魁祸首和不必要的缺陷。
另一个值得一提的是,前两个“解决方案”使用了大量的资源。脚本像5.5G RAM一样拥抱。使用改进的readline()版本,它运行在30 M以下。

rur96b6h

rur96b6h2#

这两种说法似乎相互矛盾(或者我不明白):

    • 为了澄清,每个文件夹名称是一个订单 *
  • $target = $csv | Where-Object CLIENT -eq $folder

无论如何(以“* 澄清一下,每个文件夹名称都是一个ORDER*”为例),使用查找表可能会在性能方面产生很大的差异。要做到这一点,您可以使用此Join-Object script/Join-Object Module或构建自己的查找命令,请参阅:In Powershell, what's the best way to join two tables into one?
假设您有以下(子)文件夹:

Get-ChildItem $rootfolder

    Directory: C:\rootfolder

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-r--           3/29/2023  2:59 PM                11223344
d-r--           3/29/2023  3:00 PM                12345678
d-r--           3/29/2023  2:59 PM                22334455
d-r--           3/29/2023  2:59 PM                33445566
d-r--           3/29/2023  3:00 PM                44556677

下面的Csv文件:

# $csv = Import-csv -path $csvpath
$csv = ConvertFrom-Csv @'
"ORDER","CLIENT"
11223344,1111
22334455,1111
33445566,2222
44556677,1390
'@

您可能需要执行左连接(这将显示缺失的客户端):

Get-ChildItem -Directory $rootfolder |
    LeftJoin $Csv -on Name -eq Order -Property FullName, Order, Client

FullName               Order    Client
--------               -----    ------
C:\rootfolder\11223344 11223344 1111
C:\rootfolder\12345678 12345678
C:\rootfolder\22334455 22334455 1111
C:\rootfolder\33445566 33445566 2222
C:\rootfolder\44556677 44556677 1390

这意味着从这里您可以执行沿着操作:

Get-ChildItem -Directory $rootfolder |LeftJoin $Csv -on Name -eq Order |Foreach-Object {
    if ($_.Client) {
        Write-Host "Copy $($_.FullName) to $($_.Client)"
    }
    else {
        Write-Warning "No client found for $($_.Name)"
    }
}

Copy C:\rootfolder\11223344 to 1111
WARNING: No client found for 12345678
Copy C:\rootfolder\22334455 to 1111
Copy C:\rootfolder\33445566 to 2222
Copy C:\rootfolder\44556677 to 1390

相关问题