在Powershell traverse子文件夹中合并CSV文件-存档&;删除旧文件使用目标CSV的文件夹名称

在Powershell traverse子文件夹中合并CSV文件-存档&;删除旧文件使用目标CSV的文件夹名称,powershell,csv,Powershell,Csv,我想将许多CSV文件合并成一个(几百个文件),删除添加的CSV的标题行 由于文件位于多个子文件夹中,我需要从根开始遍历所有子文件夹并处理其中的所有CSV。在合并之前,我想用zip对它们进行归档,删除旧的CSV。新合并的CSV文件和zip归档文件的名称应与其父文件夹相同 如果为同一文件夹再次启动脚本,则不应意外损坏或删除任何已处理的文件 我不是一个Powershell的家伙,所以我一直在从网上的几个资源中复制粘贴,并提出了以下解决方案(抱歉,不记得这些资源,如果您知道的话,可以在评论中随意添加引用

我想将许多CSV文件合并成一个(几百个文件),删除添加的CSV的标题行

由于文件位于多个子文件夹中,我需要从根开始遍历所有子文件夹并处理其中的所有CSV。在合并之前,我想用zip对它们进行归档,删除旧的CSV。新合并的CSV文件和zip归档文件的名称应与其父文件夹相同

如果为同一文件夹再次启动脚本,则不应意外损坏或删除任何已处理的文件

我不是一个Powershell的家伙,所以我一直在从网上的几个资源中复制粘贴,并提出了以下解决方案(抱歉,不记得这些资源,如果您知道的话,可以在评论中随意添加引用)

这个补丁工作代码可以完成这项工作,但感觉不是很可靠。目前,它只处理子文件夹中的CSV文件。在给定的
$targDir
中处理文件也很好

我想知道它是否可以更紧凑一些。欢迎提出改进建议

$targDir = "\\Servername\folder\";  #path

Get-ChildItem "$targDir" -Recurse -Directory |
    ForEach-Object { #walkinthrough all subfolder-paths
        
        #
        Set-Location -Path $_.FullName
        
        #remove existing AllInOne.csv (targed name for a merged file) in case it has been left over from a previous execution.
        $FileName = ".\AllInOne.csv"
        if (Test-Path $FileName) {
            Remove-Item $FileName
        }

        #remove existing AllInOne.csv (targed name for archived files) in case it has been left over from a previous execution.
        $FileName = ".\AllInOne.zip"
        if (Test-Path $FileName) {
            Remove-Item $FileName
        }
        
        #compressing all csv files in the current path, temporarily named AllInOne.zip. Doing that for each file adding it to the archive (with -Update)
        # I wonder if there is a more efficient way to do that.
        dir $_.FullName | where { $_.Extension -eq ".csv"} | foreach { Compress-Archive $_.FullName -DestinationPath "AllInOne.zip" -Update}
        
        ##########################################################
        # This code is basically merging all the CSV files 
        # skipping the header of added files
        ##########################################################
        $getFirstLine = $true

        get-childItem ".\*.csv" | foreach {
        $filePath = $_

        $lines =  $lines = Get-Content $filePath  
        $linesToWrite = switch($getFirstLine) {
            $true  {$lines}
            $false {$lines | Select -Skip 1}
        }

        $getFirstLine = $false
        Add-Content ".\AllInOne.csv" $linesToWrite

        # Output file is named AllInOne.csv temporarily - this is not a requirement
        # It was simply easier for me to come up with this temp file in the first place (symptomatic for copy&paste).
        }
        #########################################################

        #deleting old csv files
        dir $_.FullName | where { $_.Extension -eq ".csv" -and $_ -notlike "AllInOne.csv"} | foreach { Remove-Item $_.FullName}

        # Temporarily rename AllinOne files with parent folder name
        Get-ChildItem -Path $_.FullName -Filter *.csv | Rename-Item -NewName {$_.Basename.Replace("AllInOne",$_.Directory.Name) + $_.extension}
        Get-ChildItem -Path $_.FullName -Filter *.zip | Rename-Item -NewName {$_.Basename.Replace("AllInOne",$_.Directory.Name) + $_.extension}         
    } 
我一直在Powershell ISE中执行它。这个剧本只是一个管家的角色,随意地执行,而不是在固定的基础上执行——所以表演并不重要


如果可能的话,我更喜欢使用不依赖其他库的脚本(例如,对于Zip)。

它可能不是防弹的,但我见过更糟糕的拼凑脚本。它肯定会完成你想要的工作,但是这里有一些小的改变,会使它变得更短,更难打破

  • 由于您的所有文件都是CSV,并且都具有相同的标题,因此可以使用
    Import CSV
    将所有文件编译成一个数组。您不必担心剥离标题或意外删除行
  • 然后您可以使用
    Export CSV-Path$\ FullName-NoTypeInformation
    将所有内容输出到一个新的CSV文件中

  • 为了让它检查根文件夹和所有子文件夹,我将把主ForEach循环中的所有行都放到一个函数中,然后为根文件夹调用它一次,并为所有子文件夹保留现有循环
  • 这更多的是一种风格选择,但我会以稍微不同的顺序来完成这个脚本的步骤:
  • 获取父文件夹名称
  • 删除旧的编译CSV和ZIP
  • 将CSV编译到一个数组中,并以父文件夹名称输出
  • 将CSV压缩到具有父文件夹名称的文件中
  • 删除所有CSV文件

  • 就个人而言,我宁愿在第一次正确命名创建的文件,而不是必须返回并重命名它们,除非完全没有办法。这似乎不适合您的情况,因此您应该能够在第一次使用正确的名称创建它们。

    非常感谢。这很有帮助。我没有使用$csvArray,而是直接使用导出CSV进行管道导入。这对我来说更容易发现CSV中的不一致。导入CSV$124;导出CSV-路径。/AllInOne.CSV-NoTypeInformation-追加
    Get-ChildItem "*.csv" | Foreach-Object {
        $csvArray += Import-CSV $_
    }
    
    function CompileCompressCSV {
        param (
            [string] $Path
        )
    
        # Code from inside the ForEach Loop
    }
    
    # Main Script
    
    CompileCompressCSV -Path $targetDir
    
    Get-ChildItem -Path $targetDir -Recurse -Directory | ForEach-Object {
        
        CompileCompressCSV -Path $_.FullName
    }