Powershell: Export a custom object to a CSV file -

2020-01-27 08:24发布

问题:

I wrote a script that constructs a custom object and exports it to a CSV file:

$reg = Get-ItemProperty HKLM:\SOFTWARE\McAfee\DLP\Agent | Select-Object agentversion

$date = Get-ItemProperty 'C:\Program Files\McAfee' | Select-Object {$_.LastWriteTime} 

$date86 = Get-ItemProperty 'C:\Program Files (x86)\McAfee'| Select-Object {$_.LastWriteTime}

New-Object -TypeName pscustomobject -Property @{
  "Number1"=$reg
  "Number2"=$date86
  "Number3"=$date } | export-csv -Path C:\****\desktop\stuff.csv -NoTypeInformation

and the data row in the resulting CSV file is:

"@{AgentVersion=9.4.112.22}","@{$.LastWriteTime=5/6/2016 6:02:32 AM}","@{$.LastWriteTime=7/5/2016 8:34:01 PM}"

Is it possible to get rid of the unwanted @{<name>=...} wrappers?

回答1:

The expression:

Select-Object {$_.LastWriteTime}

outputs an object with a single property with name $_.LastWriteTime. The simplest way to fix it is to use the -ExpandProperty parameter which will only output the value that you are interested in. e.g.:

$date86 = Get-ItemProperty 'C:\Program Files (x86)\McAfee'|
            Select-Object -ExpandProperty LastWriteTime 

Note that I've also removed the script block from the Select-Object command, as it wasn't necessary.



回答2:

To complement zdan's helpful answer[1] with alternatives:

If you just want the value of a given object's property, simply wrap the command in (...) and use .<propertyName>:

(Get-ItemProperty 'C:\Program Files\McAfee').LastWriteTime # returns a [datetime] instance

In PSv3+, the above also works with commands returning multiple items (arrays), in which case an array of the input items' individual property values is output - this feature is called member enumeration.


PSv3 introduced a shortcut syntax for % / ForEach-Object (and also ? / Where-Object) that can be leveraged here as well :

Get-ItemProperty 'C:\Program Files\McAfee' | % LastWriteTime # ditto

This is the equivalent of the more verbose (which also works in PSv2-):

Get-ItemProperty 'C:\Program Files\McAfee' | % { $_.LastWriteTime }

These two pipeline-based syntax forms are slower, but have two advantages:

  • Large input collections are better processed in pipelines one by one in order to keep memory use constant (if feasible; if you need to collect the entire output in memory, there is no advantage).

  • This syntax unambiguously references an individual item's property rather than a property of the collection as a whole.

    • E.g., (Get-ChildItem -File C:\Windows).Length returns the count of files in C:\Windows, because Length is interpreted as the collection's (array's) property;
      by contrast, Get-ChildItem -File C:\Windows | % Length returns an array of the individual files' .Length (file-size) property values.

Finally, in PSv4+, you may also use the .ForEach() collection method, which doesn't use the pipeline and is therefore faster (though slightly slower than member enumeration), but, like member enumeration, requires that the input collection be in memory in full:

(Get-ItemProperty 'C:\Program Files\McAfee').ForEach('LastWriteTime')

[1] A quick overview of Select-Object's behavior:

  • Select-Object [-Property] <string[]> returns a custom object for each input object, containing only the specified properties; even with only a single property specified, the results are custom objects with that single property, not the property values themselves.

  • By contrast, using -ExpandProperty <string> returns the given, single property's value from each input object (typed as-is) instead.

A simple example: extract the Year property value from a Get-Date call:

# WRONG: with (implied) -Property
PS> $val = Get-Date | Select-Object Year; "$val"
@{Year=2018}  # !!
# A custom object with a Year property was returned and the above is its
# string representation, the equivalent of:
#      "$([pscustomobject] @{ Year = 2018 })"

# CORRECT: with -ExpandProperty
PS> $val = Get-Date | Select-Object -ExpandProperty Year; "$val"
2018  # OK: -ExpandProperty extracted just the property's *value*