Export-CSV -Append #powershell

Export-CSV -Append

 

Here’s the solution for those who need to append rows to existing CSV files (andcannot do that): I have just used PowerShell 2.0 code snippets to create a proxy cmdlet – function which wraps standard Export-CSV cmdlet but adds handling of the-Append parameter.

So you can do something like:

Get-Process | Export-Csv -Path 'c:\Temp\processes.csv' -Append -Delimiter ';'

As you can see, other parameters – such as Delimiter – still function as well. If the file does not exist – the cmdlet will essentially ignore -Append and create the file as normal. If you specify -Append and the file is present, the function will turn the objects into CSV strings, remove the first row with the property names and append to the existing file.

For your convenience, I have posted the source code to PoshCode. Here’s it is as well for those interested:

#Requires -Version 2.0

<#
  This Export-CSV behaves exactly like native Export-CSV
  However it has one optional switch -Append
  Which lets you append new data to existing CSV file: e.g.
  Get-Process | Select ProcessName, CPU | Export-CSV processes.csv -Append
  
  For details, see

http://dmitrysotnikov.wordpress.com/2010/01/19/export-csv-append/

  (c) Dmitry Sotnikov  
#>

function Export-CSV {
[CmdletBinding(DefaultParameterSetName='Delimiter',
  SupportsShouldProcess=$true, ConfirmImpact='Medium')]
param(
 [Parameter(Mandatory=$true, ValueFromPipeline=$true,
           ValueFromPipelineByPropertyName=$true)]
 [System.Management.Automation.PSObject]
 ${InputObject},

 [Parameter(Mandatory=$true, Position=0)]
 [Alias('PSPath')]
 [System.String]
 ${Path},
 
 #region -Append (added by Dmitry Sotnikov)
 [Switch]
 ${Append},
 #endregion 

 [Switch]
 ${Force},

 [Switch]
 ${NoClobber},

 [ValidateSet('Unicode','UTF7','UTF8','ASCII','UTF32',
                  'BigEndianUnicode','Default','OEM')]
 [System.String]
 ${Encoding},

 [Parameter(ParameterSetName='Delimiter', Position=1)]
 [ValidateNotNull()]
 [System.Char]
 ${Delimiter},

 [Parameter(ParameterSetName='UseCulture')]
 [Switch]
 ${UseCulture},

 [Alias('NTI')]
 [Switch]
 ${NoTypeInformation})

begin
{
 # This variable will tell us whether we actually need to append
 # to existing file
 $AppendMode = $false
 
 try {
  $outBuffer = $null
  if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  {
      $PSBoundParameters['OutBuffer'] = 1
  }
  $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Export-Csv',
    [System.Management.Automation.CommandTypes]::Cmdlet)
        
        
 #String variable to become the target command line
 $scriptCmdPipeline = ''

 # Add new parameter handling
 #region Dmitry: Process and remove the Append parameter if it is present
 if ($Append) {
  
  $PSBoundParameters.Remove('Append') | Out-Null
    
  if ($Path) {
   if (Test-Path $Path) {        
    # Need to construct new command line
    $AppendMode = $true
    
    if ($Encoding.Length -eq 0) {
     # ASCII is default encoding for Export-CSV
     $Encoding = 'ASCII'
    }
    
    # For Append we use ConvertTo-CSV instead of Export
    $scriptCmdPipeline += 'ConvertTo-Csv -NoTypeInformation '
    
    # Inherit other CSV convertion parameters
    if ( $UseCulture ) {
     $scriptCmdPipeline += ' -UseCulture '
    }
    if ( $Delimiter ) {
     $scriptCmdPipeline += " -Delimiter '$Delimiter' "
    } 
    
    # Skip the first line (the one with the property names) 
    $scriptCmdPipeline += ' | Foreach-Object {$start=$true}'
    $scriptCmdPipeline += '{if ($start) {$start=$false} else {$_}} '
    
    # Add file output
    $scriptCmdPipeline += " | Out-File -FilePath '$Path'"
    $scriptCmdPipeline += " -Encoding '$Encoding' -Append "
    
    if ($Force) {
     $scriptCmdPipeline += ' -Force'
    }

    if ($NoClobber) {
     $scriptCmdPipeline += ' -NoClobber'
    }   
   }
  }
 } 
  

  
 $scriptCmd = {& $wrappedCmd @PSBoundParameters }
 
 if ( $AppendMode ) {
  # redefine command line
  $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
      $scriptCmdPipeline
    )
 } else {
  # execute Export-CSV as we got it because
  # either -Append is missing or file does not exist
  $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
      [string]$scriptCmd
    )
 }

 # standard pipeline initialization
 $steppablePipeline = $scriptCmd.GetSteppablePipeline(
        $myInvocation.CommandOrigin)
 $steppablePipeline.Begin($PSCmdlet)
 
 } catch {
   throw
 }
    
}

process
{
  try {
      $steppablePipeline.Process($_)
  } catch {
      throw
  }
}

end
{
  try {
      $steppablePipeline.End()
  } catch {
      throw
  }
}
<#

.ForwardHelpTargetName Export-Csv
.ForwardHelpCategory Cmdlet

#>

}

Hope this helps! ;)

 

 

20 Responses to “Export-CSV -Append”

  1. Jason ArcherJanuary 19, 2010 at 10:25 pm

    Reader beware, you can have some problems if you append a different kind of object (or number of columns) than what is already in the file.

    • Dmitry SotnikovJanuary 20, 2010 at 7:02 am

      Yes, good point Jason! In my code I am NOT checking whether the property names are the same. Or that delimiter is the same. Or that encoding is the same. And so on. It is up to user to make sure that appended data matches the original one.

      • TimAugust 4, 2011 at 2:50 pm

        How exactly would one go about formatting data to match the original? For example:

        Data in csv is from SMO dataFile object containing server, dbname, size, usedSpace, freespace.

        I want to append a TOTAL row for each instance after all of the databases have been appended. I have tried building my own object/string and appending it to the csv, but I can’t seem to get it right. Any help would be much appreciated.

        Here is what I have tried:
        $totalString = “@{Server=$servername; dbname=$total; Size=$totalsize; UsedSpace=$totalUsed; FreeSpace=$totalFree}”;
        (above, with and without, @, {}, and quotes)

        $totalString = @”
        Server : $servername
        dbname : $total
        Size : $totalsize
        UsedSpace : $totalUsed
        FreeSpace : $totalFree
        “@

        $totalstring | export-csv c:\test.csv -append;

        Thanks,
        TIm

  2. John HuberFebruary 4, 2011 at 4:49 pm

    Thanks, I am an admin, who is not a good coder. This is a great help for me and makes my life a lot easier.

    Many Thanks

  3. Alvin MagpayoApril 7, 2011 at 11:07 pm

    Hi,
    Im quite new with powershell and this post regarding Export-CSV with append is very helpful to complete my automated script reports.

    I just don’t know how to use it. What do you mean with “Add this function to your profile to make Export-CSV cmdlet handle -Append parameter” in the poshcode.org?

    I just need to make it work and use the -Append of Export-CSV and time is killing me due to my deadline.

    Thanks in advance.

  4. Alvin MagpayoApril 9, 2011 at 8:09 am

    Thanks. It worked! I was able to do it and had the -Append on Export-CSV.

    One more question though, What if I want to make sure that this profile will be used all the time? My script will run as a scheduled job and placed on different server.

    Is there a way that I can call the same profile just to make sure that the code will be executed, or is it possible that I can include that at the top of the code as one of the validation process?

    Thanks for the help. :D

    Alvin.

  5. Alvin MagpayoApril 9, 2011 at 12:20 pm

    ei,
    I got it working. Thanks. Appreciate the help. :)

    Alvin

  6. JamieMay 5, 2011 at 9:33 pm

    I am having trouble using this script. When I use the command, I get an error saying “The varialbe ‘$UseCulture’ cannot be retrieved because it has not been set”. The error references line 96 and char 21. Please help.

    Thanks,
    Jamie

    • Dmitry SotnikovMay 5, 2011 at 11:02 pm

      Jamie,

      Looks like you have strict mode on, and this somehow does not like the parameter declarations of the function.

      One easy workaround that I see is to simply add Set-StrictMode -Off to the biginning of the script… If this is unacceptable, you’ll need to figure out how to make the PowerShell parser consider the parameters ok to use in the script – let me know if you manage to do that.

      Dmitry

  7. Chad MillerMay 21, 2011 at 2:10 am

    Dimitry

    I’m wondering if you can help me understand this piece of code in from your script (modified slightly to illustrate an example):

    get-psdrive | foreach-object {$start=$true} {if ($start) {$start=$false} else {$_}}

    I understand the point–skip first row and it works perfectly. I’ve seen scriptblocks before, but this one looks odd.

    The first block is the foreach-object block and the second block, well I don’t understand how the pipeline is able to continue to pipe objects.

  8. Chad MillerMay 21, 2011 at 8:11 pm

    My previous question was answered via Twitter by Bartek

    “Looks like -begin and -process blocks. So the {$start = $true} is run only once, before obj arrive”

    Sure enough get-help foreach-object mentions these params, but the documentation incorrectly states they are named params.

  9. JCochranDecember 8, 2011 at 9:12 pm

    Am I missing something? While I greatly appreciate this code, and I could not code myself out of a brown paper bag, isn’t this an excessive amount of code to simply append a file? I mean that with all due respect and I am more than likely missing something, but this seems counter to the simplicity of the intended nature of PowerShell. I am one person that is finding PowerShell to be powerful, but not all that simple. I rarely have time to code and read and it seems like a significant amount of time is required to learn PowerShell to this degree. Maybe I’m dumb? I dunno… :p

  10. Daniel RymerJanuary 25, 2012 at 2:27 pm

    Code works great. I tried writing my own using 2 additional temp files, which was far shorter, but kept ending up with blank lines I couldn’t seem to remove. I used this code and the file came out perfect! Thanks!

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s