{"id":1729,"date":"2013-12-03T00:00:00","date_gmt":"2013-11-26T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/powershell-data-basics-file-based-data\/"},"modified":"2017-10-25T15:16:45","modified_gmt":"2017-10-25T15:16:45","slug":"powershell-data-basics-file-based-data","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/powershell-data-basics-file-based-data\/","title":{"rendered":"PowerShell Data Basics: File-Based Data"},"content":{"rendered":"<div id=\"pretty\">\n<h2>Introduction<\/h2>\n<p class=\"MsoNormal\">As I have continued working with PowerShell I realized that at the core is <code>the data <\/code>(as with any software really). In the case of PowerShell, this boils down to converting external data\u00a0 into PowerShell objects and vice versa. This is the first of a series of articles that shows you how to <i>import<\/i> almost all of the common data formats you are likely to encounter, and how to <i>export<\/i> to some of them as well.<\/p>\n<h2>Part 1: General Data<\/h2>\n<p class=\"MsoNormal\">This article explains how to import data from files of all kinds except for XML, which is covered separately in the next article in this series. The sections below describe a variety of text formats, from fixed-width, variable-width, and ragged-right files to CSV, property lists, INI files, and JSON data, and concludes with a treatment of importing and exporting to Excel.<\/p>\n<p class=\"MsoNormal\">Part 2 of this series will \u00a0illustrate the two principal \u00a0technologies available within PowerShell to access XML data, XPath and objects. You will learn how to move XML data into and out of PowerShell along with how to reference, manipulate, and modify it.<\/p>\n<h2>Data by Line<\/h2>\n<h3>Read a Text File in its Entirety<\/h3>\n<p class=\"MsoNormal\">Regular readers will know of my fondness for Lewis Carroll; the current topic again draws me unerringly to this inadvertent comment on software methodology:<\/p>\n<p class=\"MsoNormal\"><i> &#8216;Begin at the beginning,&#8217; the King said gravely, &#8216;and go on till you come to the end: then stop.&#8217;<\/i><\/p>\n<p class=\"MsoNormal\">&#8211;Lewis Carroll, <b>Alice&#8217;s Adventures in Wonderland<\/b><\/p>\n<p class=\"MsoNormal\">In the context of PowerShell, the beginning is where you learn to read a file in PowerShell. My sample file consists of fifteen lines containing the text &#8220;one&#8221;, &#8220;two&#8221;, etc., on up to &#8220;fifteen&#8221;. The <code>Get-Content<\/code> cmdlet is the primary workhorse for bringing data from a file into PowerShell:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>Get-Content -Path .\\numbers.txt<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>one<\/code><\/p>\n<p><code>two<\/code><\/p>\n<p><code>three<\/code><\/p>\n<p><code>four<\/code><\/p>\n<p><code>five<\/code><\/p>\n<p><code>six<\/code><\/p>\n<p><code>seven<\/code><\/p>\n<p><code>eight<\/code><\/p>\n<p><code>nine<\/code><\/p>\n<p><code>ten<\/code><\/p>\n<p><code>eleven<\/code><\/p>\n<p><code>twelve<\/code><\/p>\n<p><code> \t\tthirteen<\/code><\/p>\n<p><code> \t\tfourteen<\/code><\/p>\n<p><code>fifteen<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">In the absence of any other direction, <code>Get-Content<\/code> displays its result on the console. You could redirect this output to a file using exactly the same mechanism as in a DOS or Unix or Linux shell &#8230;<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Content -Path .\\numbers.txt&gt; newfile.txt <\/pre>\n<p class=\"MsoNormal\">&#8230; or you could send this output through the pipeline to another process, likewise:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Content -Path .\\numbers.txt| ... <\/pre>\n<p class=\"MsoNormal\">Yet another common outlet is collecting the output into a variable for later processing:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$numbers = Get-Content-Path .\\numbers.txt <\/pre>\n<h3>Read a Text File Selectively by Index<\/h3>\n<p class=\"MsoNormal\">PowerShell uses the <code>$ <\/code>prefix character to indicate a variable. <code>Get-Content<\/code> conveniently stores its output into $numbers as an array. Here are a couple of expressions to illustrate this:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>$numbers.Count # report the cardinality<\/code><\/b><\/p>\n<p><b><code>$numbers[0]\u00a0\u00a0\u00a0 # display the first item<\/code><\/b><\/p>\n<p><b><code>$numbers[14]\u00a0\u00a0 # display the last item<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>15<\/code><\/p>\n<p><code>one<\/code><\/p>\n<p><code>fifteen<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">PowerShell also offers a very handy range operator allowing you to select a portion of the array as a subset:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>$numbers[0..3]<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>one<\/code><\/p>\n<p><code>two<\/code><\/p>\n<p><code>three<\/code><\/p>\n<p><code>four<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Applying that concept back to the original file, you can directly index the content of the file by line number-the parentheses are needed to first materialize the array that you then index with the bracket notation:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>(Get-Content -Path .\\numbers.txt)[0..3]<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>one<\/code><\/p>\n<p><code>two<\/code><\/p>\n<p><code>three<\/code><\/p>\n<p><code>four<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">The array indexing notation gives you the most succinct way to select specific lines.\u00a0 It is almost as though you are doing a pipeline, i.e. <code> read-a-file | select-lines-from-file<\/code>. You can, in fact, do the same operation with a full-fledged pipeline:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Content-Path .\\numbers.txt |Select-Object -First 4\u00a0 <\/pre>\n<p class=\"MsoNormal\">Here you are passing the file contents to the <code>Select-Object<\/code> cmdlet and instructing it to take the first four elements, just as done in the previous example. That works for lines at the beginning, and you also have a -Last parameter to grab lines from the end of the file, but what about something in the middle? With array notation you can specify an arbitrary range <code>[m..n]<\/code>. With <code>Select-Object<\/code> this equates to two pipelined calls to <code>Select-Object<\/code>. Here is code to demonstrate the equivalence of the two methods:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$m=5; $n=7\r\nGet-Content .\\numbers.txt | select -First ($n+1) | select -Last ($n-$m+1)\r\n(Get-Content .\\numbers.txt)[$m..$n] \r\n<\/pre>\n<p class=\"MsoNormal\">Obviously in this case the array indexing notation is much cleaner and more concise, but this example is valuable to introduce you to ways to manipulate data in PowerShell. You might use this technique to skip the header row when reading in a file, for example.<\/p>\n<h3>Read a Text File Selectively By Search<\/h3>\n<p class=\"MsoNormal\">Now consider the scenario where rather than wanting to get specific lines by number you want to get all lines that contain a particular string. Use the <code> Select-String<\/code> cmdlet (similar to the Linux <code>grep<\/code> command or the DOS <code>findstr<\/code> command).<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>Get-Content -Path .\\numbers.txt | Select-String teen<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\tthirteen<\/code><\/p>\n<p><code> \t\tfourteen<\/code><\/p>\n<p><code>fifteen<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">You are not limited to string constants. In fact, the default search term is a regular expression unless you specify that you want to match just\u00a0 a simple string with the <code>-SimpleMatch<\/code> parameter . But if your parameter does not contain any regex metacharacters you can omit the &#8220;-<code>SimpleMatch<\/code>&#8221; label as I have done in the above example. So if you want to select the lines ending in teen that also contain an &#8220;i&#8221; earlier in the string, use this:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>Get-Content -Path .\\numbers.txt | Select-String -pattern \"i.*teen$\"<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\tthirteen<\/code><\/p>\n<p><code>fifteen<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Data by Fixed Width Fields<\/h2>\n<h3>The PowerShell Approach<\/h3>\n<p class=\"MsoNormal\">The next logical step is to not just read a file by lines, but to split up those lines into fields. Say you have a text file where each line is a record and each record consists of, for example, 7 characters for the given name, 10 characters for the surname, and 3 characters for an ID of some kind.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>A Fixed Width File<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\t12345671234567890123<\/code><\/p>\n<p><code>george  \t\tjetson\u00a0\u00a0\u00a0 5\u00a0  \t\t<\/code><\/p>\n<p><code>warren  \t\tbuffett\u00a0\u00a0 123<\/code><\/p>\n<p><code> \t\thoratioalger\u00a0\u00a0\u00a0\u00a0 -99<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><\/p>\n<pre class=\"lang:ps theme:powershell-ise\">#process the file\r\nGet-Content .\\fixedwidth.log | #read each line as a separate object\r\n\u00a0\u00a0 select -Property @{name='ID';expression={$_.Substring(17).Trim()}}, #first column\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{name='FirstName';expression={$_.Substring(0,7).Trim()}}, #2nd \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{name='LastName';expression={$_.Substring(7,10).Trim()}} #3rd\r\n<\/pre>\n<p><\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>A Fixed Width File Converted to Objects<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Id\u00a0 \t\tFirstName LastName\u00a0 <\/code><\/p>\n<p><code>--\u00a0 \t\t--------- --------\u00a0 <\/code><\/p>\n<p><code>123  \t\t1234567\u00a0\u00a0 1234567890<\/code><\/p>\n<p><code>5\u00a0\u00a0 \t\tgeorge\u00a0\u00a0\u00a0 jetson\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code>123  \t\twarren\u00a0\u00a0\u00a0 buffett\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code>-99  \t\thoratio\u00a0\u00a0 alger<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>The Regex Approach<\/h3>\n<p class=\"MsoNormal\">\u00a0This approach does use regular expressions and the mere mention of them causes otherwise hearty, courageous, and fearless developers to quake in their boots. Or so I have heard. But for the task at hand what you need to know about regular expressions is simple. Really. For each field in the file you need to give it a name and you need to know its length. Take those two values and plug them into this template:<\/p>\n<pre>(?&lt;field name goes here&gt;.{field length goes here})<\/pre>\n<p class=\"MsoNormal\">Repeat that for each field then lay each one down adjacent to the previous one. Here I am representing names with &#8220;n&#8221; and lengths with &#8220;l&#8221;:<\/p>\n<pre>(?&lt;n1&gt;.{l1})(?&lt;n2&gt;.{l2})(?&lt;n3&gt;.{l3})<\/pre>\n<p class=\"MsoNormal\">Finally, add a caret (^) at the front end and a dollar sign ($) at the rear:<\/p>\n<pre>^(?&lt;n1&gt;.{l1})(?&lt;n2&gt;.{l2})(?&lt;n3&gt;.{l3})$<\/pre>\n<p class=\"MsoNormal\">Here is the realization of this for the example at hand:<\/p>\n<pre>$regex= \"^(?&lt;FirstName&gt;.{7})(?&lt;LastName&gt;.{10})(?&lt;Id&gt;.{3})$\"<\/pre>\n<p class=\"MsoNormal\">After you define the regex, the code to use it to parse the same text file is almost trivial, but this reads the file and extracts all the fields from each line (record) into an array of PowerShell objects-all thanks to the magic of the built-in <code>-match<\/code> operator.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function ImportWith-Regex([string]$FilePath, [string]$regex)\r\n{\r\n\u00a0\u00a0\u00a0 Get-Content $FilePath | ForEach-Object {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ($PSItem -match $regex)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 New-Object PSObject -Property (Get-RegexNamedGroups $matches)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\u00a0\u00a0\u00a0 \r\n\u00a0\u00a0\u00a0 }\r\n}\u00a0 \r\n<\/pre>\n<p class=\"MsoNormal\">Note that this code needs a supplemental function to do its work, shown next, that collects just the parts of the regex match results that are needed. (Don&#8217;t worry about exactly how this works).<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function Get-RegexNamedGroups($hash)\r\n{\r\n\u00a0\u00a0\u00a0 $newHash = @{};\r\n\u00a0\u00a0\u00a0 $hash.keys | ? { $_ -notmatch '^\\d+$' } | % { $newHash[$_] = $hash[$_] }\r\n\u00a0\u00a0\u00a0 $newHash \r\n} \r\n<\/pre>\n<p class=\"MsoNormal\">The major advantage of the above approach is that you have complete separation between your data specification (the regex definition) and the code to process it. You can use the same function on a different file by just passing in a different regex and a different file name.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$regex = \"^(?&lt;FirstName&gt;.{7})(?&lt;LastName&gt;.{10})(?&lt;Id&gt;.{3})$\"\r\nImportWith-Regex .\\fixedwidth.log $regex\r\n<\/pre>\n<p class=\"MsoNormal\">However, it is worth revisiting the earlier code-that used substrings-and seeing how you could do it with regular expressions.\u00a0 Just make sure that your regex and your explicit field names in the <code>select<\/code> statement remain in sync.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$regex = \"^(?&lt;FirstName&gt;.{7})(?&lt;LastName&gt;.{10})(?&lt;Id&gt;.{3})$\"\r\nGet-Content .\\fixedwidth.log |\r\n\u00a0\u00a0 ForEach-Object {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 if ($_ -match $regex) {$matches} else {Throw 'line $_ was corrupt'}\r\n\u00a0\u00a0 } |\r\n\u00a0\u00a0 select -Property @{name='FirstName';expression={$_.FirstName}}, \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{name='LastName'; expression={$_.LastName} }, \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{name='Id';\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 expression={$_.Id}\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\t<\/pre>\n<h3>The Ragged Right Variation<\/h3>\n<p class=\"MsoNormal\">One important variation of fixed width files is the <i>ragged right<\/i> format, which defines all columns by width <i>except<\/i> for the last column, which simply runs to the end of the line. Accommodating this format requires just a trivial change to the last code sample. Modify the last capture group in the regular expression to use <code>.*<\/code> instead of <code>.{n}<\/code> as shown below. I have also renamed it from <code>Id<\/code> to <code>Description<\/code> since that is now a more likely field name.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$regex = \"^(?&lt;FirstName&gt;.{7})(?&lt;LastName&gt;.{10})(?&lt;Description&gt;.*)$\"\r\nImportWith-Regex .\\fixedwidth.log $regex\r\n<\/pre>\n<p class=\"MsoNormal\">Here&#8217;s the second form:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$regex = \"^(?&lt;FirstName&gt;.{7})(?&lt;LastName&gt;.{10})(?&lt;Description&gt;.*)$\"\r\nGet-Content .\\fixedwidth.log |\r\n\u00a0\u00a0 ForEach-Object {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 if ($_ -match $regex) {$matches} else {Throw 'line $_ was corrupt'}\r\n\u00a0\u00a0 } |\r\n\u00a0\u00a0 select -Property @{name='FirstName';\u00a0\u00a0 expression={$_.FirstName}\u00a0 }, \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{name='LastName';\u00a0\u00a0 \u00a0expression={$_.LastName}\u00a0\u00a0 }, \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{name='Description'; expression={$_.Description}}\r\n\u00a0\r\n<\/pre>\n<h2>Data by Variable Width Fields<\/h2>\n<h3>Importing from CSV<\/h3>\n<p class=\"MsoNormal\">Pipe your data or your file into <code> ConvertFrom-Csv<\/code> (if immediate data) or <code>Import-Csv<\/code> (if file data), to yield an array of PowerShell objects-no muss, no fuss. This first example uses multi-record input from a string constant for clarity; loading data from a file is just as straightforward.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>@&#8217;<\/p>\n<p>Shape,Color,Count<\/p>\n<p>Square,Green,4<\/p>\n<p>Rectangle,,12<\/p>\n<p>Parallelogram,,0<\/p>\n<p>Trapezoid,Black,100<\/p>\n<p>&#8216;@ | <b> ConvertFrom-Csv<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Shape\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tColor\u00a0\u00a0\u00a0\u00a0 Count<\/code><\/p>\n<p><code>-----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t-----\u00a0\u00a0\u00a0\u00a0 -----<\/code><\/p>\n<p><code>Square\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tGreen\u00a0\u00a0\u00a0\u00a0 4\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\tRectangle\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t12\u00a0\u00a0 <\/code><\/p>\n<p><code> \t\tParallelogram\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a00\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\tTrapezoid\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Black\u00a0\u00a0\u00a0\u00a0 \t\t100\u00a0 <\/code> <code><\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Things to observe from this example:<\/p>\n<ul>\n<li>The first row of data contains column headers that become properties in PowerShell.<\/li>\n<li>Each input record generates a PowerShell object with those properties.<\/li>\n<li>The actual output of <code>ConvertFrom-Csv<\/code> (or<code>  \t\tImport-Csv<\/code>) is an array of those PowerShell objects.<\/li>\n<li>All data are <strong>strings <\/strong>even if they look like other types (e.g. integers). That is why the values of the Count column\/property are left-justified. See the next section for a workaround.<\/li>\n<\/ul>\n<p class=\"MsoNormal\"><code>ConvertFrom-Csv<\/code> (and <code>Import-Csv<\/code>) gives you the option to include the header row in your data or not, at your choice. This next example shows how to separate the header row from the data by including it as part of the command invocation using the <code>-Header<\/code> parameter.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>@&#8217;<\/p>\n<p>Square,Green,4<\/p>\n<p>Rectangle,,12<\/p>\n<p>Parallelogram,,0<\/p>\n<p>Trapezoid,Black,100<\/p>\n<p>&#8216;@ | <b> ConvertFrom-Csv<\/b> <i><code> \t\t-Header<\/code><\/i> Shape, Color, Count<b><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Shape\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tColor\u00a0\u00a0\u00a0\u00a0 Count<\/code><\/p>\n<p><code>-----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t-----\u00a0\u00a0\u00a0\u00a0 -----<\/code><\/p>\n<p><code>Square\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tGreen\u00a0\u00a0\u00a0\u00a0 4\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\tRectangle \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a012\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\tParallelogram\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t0\u00a0\u00a0\u00a0 <\/code><\/p>\n<p><code> \t\tTrapezoid\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Black\u00a0\u00a0\u00a0\u00a0 \t\t100\u00a0 <\/code> <code><\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">One special case of interest is worth considering here: directly populating a hash table from a CSV file. In this case, your data records should consist of two fields each. (Any additional fields for a given record will be ignored; any record with just a single field will assign a null value to that hash entry.)<\/p>\n<p class=\"MsoNormal\">\u00a0If your CSV file <i>has<\/i> a header record (i.e. column names in the first record), use this (note that this also gives you the flexibility to select <i>any<\/i> two fields by name in the record if more than two are present):<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>$hash = @{};<\/p>\n<p><b> <code>Import-Csv<\/code><\/b> data.csv |<\/p>\n<p><b> <code>%<\/code><\/b> { $hash[$_.first] = $_.second } # Assumes header row &#8220;first,second&#8221;<\/p>\n<p>$hash[&#8220;Square&#8221;] # output one of the stored values<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Green<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3><a id=\"Toc328995789\"><\/a>Exporting to CSV<\/h3>\n<p class=\"MsoNormal\">There are two cmdlets for exporting data to CSV: <code>ConvertTo-Csv<\/code> sends its output to <code> stdout<\/code> while <code>Export-Csv<\/code> sends its output specifically to a file. Otherwise, they operate the same. In the example below you start with the output of the <code>Get-Process<\/code> cmdlet, filter <i>by row<\/i> with <code>Where-Object<\/code> to just those processes beginning with &#8220;W&#8221;, filter <i>by column<\/i> with <code>Select-Object<\/code> , and finally pipe to the <code>ConvertTo-Csv<\/code> cmdlet to generate the output shown.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> <code>Get-Process<\/code><\/b> |<\/p>\n<p><b> <code>Where-Object<\/code><\/b> { $_.name -like &#8220;W*&#8221; } |<\/p>\n<p><b> <code>Select-Object<\/code><\/b> name, path, vm, fileversion, id, handles |<\/p>\n<p><b><code>ConvertTo-Csv<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>#TYPE  \t\tSelected.System.Diagnostics.Process<\/code><\/p>\n<p><code> \t\t\"Name\",\"Path\",\"VM\",\"FileVersion\",\"Id\",\"Handles\"<\/code><\/p>\n<p><code> \t\t\"wininit\",,\"34832384\",,\"488\",\"77\"<\/code><\/p>\n<p><code> \t\t\"winlogon\",,\"44924928\",,\"576\",\"112\"<\/code><\/p>\n<p><code> \t\t\"WLIDSVC\",,\"64946176\",,\"1844\",\"337\"<\/code><\/p>\n<p><code> \t\t\"WLIDSVCM\",,\"27185152\",,\"2088\",\"53\"<\/code><\/p>\n<p><code> \t\t\"WLTRAY\",\"C:\\Windows\\System32\\WLTRAY.EXE\",\"168869888\",\"4.170.25.12\",\"3400\",\"305\"<\/code><\/p>\n<p><code> \t\t\"WLTRYSVC\",,\"17956864\",,\"1928\",\"49\"<\/code><\/p>\n<p><code> \t\t\"WmiPrvSE\",,\"67387392\",,\"3980\",\"257\"<\/code><\/p>\n<p><code> \t\t\"wmpnetwk\",,\"130781184\",,\"3616\",\"496\"<\/code><code><\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Notice that:<\/p>\n<ul>\n<li>Outputs the object type as a comment in the first row. This may be suppressed with the <code>-NoTypeInformation<\/code> parameter.<\/li>\n<li>Outputs the property names as the column headers.<\/li>\n<li>Quotes every property as it emits it in the output. CSV format does not require quoting <i>every<\/i> value, only those where ambiguity would arise-see <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd347724.aspx\">The Comma Separated Value (CSV) File Format<\/a> for the list of cases where quotes are required. However, quoting every value does no harm.<\/li>\n<\/ul>\n<p class=\"MsoNormal\">Generating CSV output, then, is simply a matter of getting the data into the form you want then piping it to\u00a0 <code> ConvertTo-Csv<\/code> or <code>Export-Csv<\/code>.<\/p>\n<h3>Importing from a Log File<\/h3>\n<p class=\"MsoNormal\">There are, of course, countless variations of log files, but one class of log file that is very common is that generated by a web server. The Apache\/NCSA\u00a0 <a href=\"http:\/\/httpd.apache.org\/docs\/2.2\/logs.html#common\">common log format<\/a>, a standardized format used by Apache web servers, contains fields separated by white space but it also allows whitespace within a field. CSV files handle this case by letting you optionally enclose a field in quotation marks; commas inside such a quoted region are considered normal text characters, not field separators. The Apache log allows this as well; it is most commonly used on the <code>access request<\/code> field. Other fields however, use different bracketing. The <code>timestamp<\/code> field, for instance must use required brackets ([ and ]) and treats white space within as plain text. Here are just a few lines from a log using this common log format.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>Get-Content .\\webserver.log<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\t127.0.0.1 - frank [10\/Oct\/2012:13:55:36 -0700] \"GET \/apache_pb.gif HTTP\/1.0\" 200 2326<\/code><\/p>\n<p><code> \t\t111.111.111.111 - martha [18\/Oct\/2012:01:17:44 -0700] \"GET \/ HTTP\/1.0\" 200 101<\/code><\/p>\n<p><code> \t\t111.111.111.111 - - [18\/Oct\/2007:11:17:55 -0700] \"GET \/style.css HTTP\/1.1\" 200 4525<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Each row contains seven fields-here is the first record split apart with each field identified.<\/p>\n<table class=\"MsoTableMediumShading1Accent3\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p class=\"MsoNormal\"><b>Host or IP address<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p class=\"MsoNormal\">\u00a0127.0.0.1<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p class=\"MsoNormal\"><b>Remote log name<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p class=\"MsoNormal\">\u00a0&#8211;<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p class=\"MsoNormal\"><b>Authenticated<br \/>\n user name <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p class=\"MsoNormal\">\u00a0frank<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p class=\"MsoNormal\"><b>Timestamp <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p class=\"MsoNormal\">[10\/Oct\/2000:13:55:36 -0700]<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p class=\"MsoNormal\"><b>Access request <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p class=\"MsoNormal\">GET \/apache_pb.gif HTTP\/1.0<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p class=\"MsoNormal\"><b>Result status code <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p class=\"MsoNormal\">\u00a0200<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p class=\"MsoNormal\"><b>Bytes transferred <\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p class=\"MsoNormal\">\u00a02326<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Earlier you saw how to build a complicated-looking regular expression with a simple template to recognize fixed-width data and then pass that regex to <code> ImportWith-Regex<\/code>. Here&#8217;s the regex to recognize the Apache common log format followed by a call to <code>ImportWith-Regex<\/code>. I have wrapped them together into a function merely for convenience in the subsequent examples.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function Import-ApacheLog($FileName)\r\n{\r\n\u00a0\u00a0\u00a0 $apacheExtractor = \"(?&lt;Host&gt;\\S*)\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"(?&lt;LogName&gt;.*?)\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"(?&lt;UserId&gt;\\S*)\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"\\[(?&lt;TimeStamp&gt;.*?)\\]\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \"`\"(?&lt;Request&gt;[^`\"]*)`\"\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"(?&lt;Status&gt;\\d{3})\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"(?&lt;BytesSent&gt;\\S*)\" -join \"\\s+\"\r\n\u00a0\u00a0\u00a0 ImportWith-Regex $FileName $apacheExtractor\r\n}\r\n\t<\/pre>\n<p class=\"MsoNormal\">If you just run the above function you get output in PowerShell&#8217;s canonical <i>list format<\/i> (each field is on a separate line and records are separated by an extra blank line). This occurs typically when a record has four or more fields. However, PowerShell&#8217;s <i>table format<\/i> is often more useful-and certainly more concise. To convert the output from the former to the latter, just pipe it to the <code>Format-Table<\/code> cmdlet. When you do that, however, the width of your screen may cause truncation of the data on screen. The last snippet, then, shows how to select fewer columns with the <code>Select-Object<\/code> cmdlet to avoid that issue.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>Import-ApacheLog .\\webserver.log<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\tTimeStamp : 10\/Oct\/2012:13:55:36 -0700<\/code><\/p>\n<p><code>LogName\u00a0\u00a0 \t\t: -<\/code><\/p>\n<p><code>Host\u00a0\u00a0\u00a0  \t\t\u00a0\u00a0: 127.0.0.1<\/code><\/p>\n<p><code>UserId\u00a0\u00a0\u00a0 \t\t: frank<\/code><\/p>\n<p><code>Status\u00a0\u00a0\u00a0 \t\t: 200<\/code><\/p>\n<p><code>Request\u00a0\u00a0 \t\t: GET \/apache_pb.gif HTTP\/1.0<\/code><\/p>\n<p><code> \t\tBytesSent : 2326<\/code><\/p>\n<p><code><\/code><\/p>\n<p><code> \t\tTimeStamp : 18\/Oct\/2012:01:17:44 -0700<\/code><\/p>\n<p><code>LogName\u00a0\u00a0 \t\t: -<\/code><\/p>\n<p><code>Host\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: 111.111.111.111<\/code><\/p>\n<p><code>UserId\u00a0\u00a0\u00a0 \t\t: martha<\/code><\/p>\n<p><code>Status\u00a0\u00a0\u00a0 \t\t: 200<\/code><\/p>\n<p><code>Request\u00a0\u00a0 \t\t: GET \/ HTTP\/1.0<\/code><\/p>\n<p><code> \t\tBytesSent : 101<\/code><\/p>\n<p><code><\/code><\/p>\n<p><code> \t\tTimeStamp : 18\/Oct\/2007:11:17:55 -0600<\/code><\/p>\n<p><code>LogName\u00a0\u00a0 \t\t: -<\/code><\/p>\n<p><code>Host\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: 111.111.111.111<\/code><\/p>\n<p><code>UserId\u00a0\u00a0\u00a0 \t\t: -<\/code><\/p>\n<p><code>Status\u00a0\u00a0\u00a0 \t\t: 200<\/code><\/p>\n<p><code>Request\u00a0\u00a0 \t\t: GET \/style.css HTTP\/1.1<\/code><\/p>\n<p><code> \t\tBytesSent : 4525<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>Import-ApacheLog .\\webserver.log | Format-Table -AutoSize<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\tTimeStamp\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0LogName Host\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tUserId Status Request\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\t---------\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t------- ----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t------ ------ -------\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\t10\/Oct\/2012:13:55:36 -0700 -\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 127.0.0.1\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tfrank\u00a0 200\u00a0\u00a0\u00a0 \t\tGET \/apache_pb.g...<\/code><\/p>\n<p><code> \t\t18\/Oct\/2012:01:17:44 -0700 -\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  \t\t111.111.111.111 martha 200\u00a0\u00a0\u00a0 GET \/ HTTP\/1.0\u00a0\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\t18\/Oct\/2007:11:17:55 -0600 -\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  \t\t111.111.111.111 -\u00a0\u00a0\u00a0\u00a0\u00a0 200\u00a0\u00a0\u00a0 \t\tGET \/style.css H...<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>Import-ApacheLog .\\webserver.log | Select Host,UserId,TimeStamp,Status,Request | Format-Table  \t\t-AutoSize<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Host\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tUserId TimeStamp\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tStatus Request\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code>----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t------ ---------\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t------ -------\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\t127.0.0.1\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 frank\u00a0 \t\t10\/Oct\/2012:13:55:36 -0700 200\u00a0\u00a0\u00a0 GET \/apache_pb.gif  \t\tHTTP\/1.0<\/code><\/p>\n<p><code> \t\t111.111.111.111 martha 18\/Oct\/2012:01:17:44 -0700 200\u00a0\u00a0\u00a0  \t\tGET \/ HTTP\/1.0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code> \t\t111.111.111.111 -\u00a0\u00a0\u00a0\u00a0\u00a0 18\/Oct\/2007:11:17:55 -0600  \t\t200\u00a0\u00a0\u00a0 GET \/style.css HTTP\/1.1<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Please note that the <code>TimeStamp<\/code> column is strictly a text value at this point. A more correct approach would require converting that to an actual <code>DateTime<\/code> object.\u00a0 That could be done either by making a custom version of <code>ImportWith-Regex<\/code> or by going back to the other familiar method of importing you have seen in this article:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$apacheExtractor = \"(?&lt;Host&gt;\\S*)\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"(?&lt;LogName&gt;.*?)\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"(?&lt;UserId&gt;\\S*)\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"\\[(?&lt;TimeStamp&gt;.*?)\\]\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 \"`\"(?&lt;Request&gt;[^`\"]*)`\"\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"(?&lt;Status&gt;\\d{3})\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"(?&lt;BytesSent&gt;\\S*)\" -join \"\\s+\"\r\n\r\nGet-Content .\\webserver.log |\u00a0\u00a0 #read each line as a separate object\r\n\u00a0\u00a0 Foreach-Object{if ($_ -match $apacheExtractor) {$matches} else {throw 'Bad record $_ '}} |\r\n\u00a0\u00a0\u00a0\u00a0 select -Property @{n='UserID';e={$_.UserId}}, #first column\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{n='LogName';\u00a0 e={$_.LogName}},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{n='Time';\u00a0\u00a0\u00a0\u00a0 e={[DateTime]::ParseExact($_.TimeStamp,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"dd\/MMM\/yyyy:HH:mm:ss zzz\",\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0[System.Globalization.CultureInfo]::InvariantCulture)}},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{n='TimeStamp';e={$_.TimeStamp}},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{n='Host';\u00a0\u00a0\u00a0\u00a0 e={$_.Host}},\r\n\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@{n='Status';\u00a0\u00a0 e={$_.Status}},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{n='Request';\u00a0 e={$_.Request}},\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @{n='BytesSent';e={$_.BytesSent}} |\r\nFormat-Table -AutoSize \r\n<\/pre>\n<h2>String Data Formats<\/h2>\n<h3>Hash Table or Property List<\/h3>\n<p class=\"MsoNormal\">A hash table or dictionary is often a very handy data structure to use. Say, for example, you want to maintain a list of configuration settings within your script. The next example shows three equivalent ways to do this. The last approach-with the <code>ConvertFrom-StringData<\/code> cmdlet-minimizes the use of punctuation requiring neither brackets, quotes, nor semicolons within the data.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>$Options = @{}<\/code><\/b><\/p>\n<p><b><code>$Options[\"height\"]=1200<\/code><\/b><\/p>\n<p><b><code>$Options[\"width\"]=1600<\/code><\/b><\/p>\n<p><b><code>$Options[\"aspect\"]=\"4:3\"<\/code><\/b><\/p>\n<p><b><code>$Options[\"depth\"]=\"24-bit\"<\/code><\/b><\/p>\n<p><b><code><\/code><\/b><\/p>\n<p><b><code><\/code><\/b><\/p>\n<p><b><code># dump the contents<\/code><\/b><\/p>\n<p><b><code>$Options<\/code><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b><code>$Options = @{<\/code><\/b><\/p>\n<p><b><code>\u00a0 \"height\" = 1200;<\/code><\/b><\/p>\n<p><b><code>\u00a0 \"width\" = 1600;<\/code><\/b><\/p>\n<p><b><code>\u00a0 \"aspect\" = \"4:3\";<\/code><\/b><\/p>\n<p><b><code>\u00a0 \"depth \"=\"24-bit\"<\/code><\/b><\/p>\n<p><b><code>}<\/code><\/b><\/p>\n<p><b><code><\/code><\/b><\/p>\n<p><b><code># dump the contents<\/code><\/b><\/p>\n<p><b><code>$Options<\/code><\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b><code>$Options = @\"<\/code><\/b><\/p>\n<p><b><code>\u00a0 height = 1200<\/code><\/b><\/p>\n<p><b><code>\u00a0 width = 1600<\/code><\/b><\/p>\n<p><b><code>\u00a0 aspect = 4:3<\/code><\/b><\/p>\n<p><b><code>\u00a0 depth = 24-bit<\/code><\/b><\/p>\n<p><b><code>\"@ | ConvertFrom-StringData <\/code><\/b><\/p>\n<p><b><code><\/code><\/b><\/p>\n<p><b><code># dump the contents<\/code><\/b><\/p>\n<p><b><code>$Options<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Name\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0Value <\/code><\/p>\n<p><code>----\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0----- <\/code><\/p>\n<p><code>width \t\t\u00a0\u00a0\u00a0\u00a0\u00a01600\u00a0 <\/code><\/p>\n<p><code>depth \t\t\u00a0\u00a0\u00a0\u00a0\u00a024-bit<\/code><\/p>\n<p><code>height\u00a0\u00a0\u00a0  \t\t\u00a01200\u00a0 <\/code><\/p>\n<p><code>aspect\u00a0\u00a0\u00a0  \t\t\u00a04:3<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p><code>Name\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0Value <\/code><\/p>\n<p><code>----\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0----- <\/code><\/p>\n<p><code>width \t\t\u00a0\u00a0\u00a0\u00a0\u00a01600\u00a0 <\/code><\/p>\n<p><code>depth \t\t\u00a0\u00a0\u00a0\u00a0\u00a024-bit<\/code><\/p>\n<p><code>height\u00a0\u00a0\u00a0  \t\t\u00a01200\u00a0 <\/code><\/p>\n<p><code>aspect\u00a0\u00a0\u00a0  \t\t\u00a04:3<\/code><\/p>\n<\/td>\n<td valign=\"top\">\n<p><code>Name\u00a0\u00a0\u00a0\u00a0 \t\tValue<\/code><\/p>\n<p><code>----\u00a0\u00a0\u00a0\u00a0 \t\t-----<\/code><\/p>\n<p><code>aspect\u00a0\u00a0 \t\t4:3<\/code><\/p>\n<p><code>depth\u00a0\u00a0\u00a0 \t\t24-bit<\/code><\/p>\n<p><code>height\u00a0\u00a0 \t\t1200<\/code><\/p>\n<p><code>width\u00a0\u00a0  \t\t\u00a01600 <\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Such a list of configuration properties could be even more useful if you put them in a separate configuration file so you can edit the configuration file independently of the program. Here, for example, are four properties given some initial value:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>Get-Content .\\properties.txt<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\tcolor=green<\/code><\/p>\n<p><code> \t\tfood=biscuit<\/code><\/p>\n<p><code> \t\tflavor=bittersweet<\/code><\/p>\n<p><code> \t\tvoice=mellifluous<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">The <code>ConvertFrom-StringData<\/code> cmdlet operates on a single string containing multiple lines of text strings rather than a file, so your sequence begins by importing the file (<code>Get-Content<\/code> or <code>gc<\/code>) and converting it to a single string (<code>Out-String<\/code>). Store the result into a variable and you have a ready-made dictionary of configuration values.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>$myConfig = gc.\\properties.txt | Out-String | ConvertFrom-StringData<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Name<\/code><code>\u00a0<\/code><code>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/code> <code>Value\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code>----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t-----<\/code><\/p>\n<p><code>color\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tgreen\u00a0<\/code><\/p>\n<p><code>food\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tbiscuit<\/code><\/p>\n<p><code>flavor\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tbittersweet<\/code><\/p>\n<p><code>voice\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tmellifluous<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>$myConfig[\"color\"]<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>green\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>INI Files<\/h3>\n<p class=\"MsoNormal\">The <a href=\"http:\/\/en.wikipedia.org\/wiki\/INI_file\">INI file format<\/a> is an old though still popular standard for configuration files used by Windows applications. INI files are simple text files composed of properties grouped into sections. In the example, there are two section, <code>Install<\/code> (with four properties) and <code>Extras<\/code> (with two properties). If you import the file with Get-Content, as shown, you just get lines of text.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>Get-Content .\\sample.ini<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\t[Install]<\/code><\/p>\n<p><code> \t\tOptions=22744<\/code><\/p>\n<p><code> \t\tIgnore=65534<\/code><\/p>\n<p><code> \t\tHardware=640|480|4|0<\/code><\/p>\n<p><code> \t\tSoftware=0|0|0|0|0|0|0<\/code><\/p>\n<p><code><\/code><\/p>\n<p><code> \t\t[Extras]<\/code><\/p>\n<p><code> \t\tOptions=10<\/code><\/p>\n<p><code> \t\tDllPath=0<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">However, if you instead import it with the <code>Get-IniFile<\/code> cmdlet that I&#8217;ll describe in a moment, you get a hash table indexed by section names, whose entries are themselves hash tables indexed by property names. Let&#8217;s see that in slow motion. The first command reads the INI file and displays it to the console. The second sequence stores it to a variable for convenience and then displays a value from the hash table. The final sequence shows a reference to one of the nested hash tables.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>Get-IniFile .\\sample.ini<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tValue <\/code><\/p>\n<p><code>----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t-----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/code><\/p>\n<p><code>Install\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t{Ignore, Software, Options, Hardware}<\/code><\/p>\n<p><code>Extras\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t{Options, DllPath}<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>$inifile = Get-IniFile .\\sample.ini<\/code><\/b><\/p>\n<p><b><code>$inifile[\"Install\"]<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tValue\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/code><\/p>\n<p><code>----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t-----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t<\/code><\/p>\n<p><code>Ignore\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t65534<\/code><\/p>\n<p><code> \t\tSoftware\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a00|0|0|0|0|0|0<\/code><\/p>\n<p><code>Options\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t22744 <\/code><\/p>\n<p><code> \t\tHardware\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t640|480|4|0<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>$inifile[\"Install\"][\"Hardware\"]<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\t640|480|4|0<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">Here is the code for <code>Get-IniFile<\/code> (adapted from <a href=\"http:\/\/stackoverflow.com\/questions\/417798\/ini-file-parsing-in-powershell\"> this StackOverflow post<\/a>). Note that if your file has properties occurring before any section is defined, those properties are put in a section labeled &#8220;-unknown-&#8220;.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Function Get-IniFile ([string]$fileName) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ini = @{}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 switch -regex -file $fileName {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"^\\[(.+)\\]$\" {\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # recognize a section\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $section = $matches[1]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ini[$section] = @{}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"^\\s*([^#]+?)\\s*=\\s*(.*)\" {\u00a0\u00a0 # recognize a property\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $name,$value = $matches[1..2]\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (!(Test-path variable:\\section)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $section = \"-unknown-\"\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ini[$section] = @{}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ini[$section][$name] = $value.trim()\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ini\r\n} \r\n<\/pre>\n<h3>JSON Data<\/h3>\n<p class=\"MsoNormal\">The <a href=\"http:\/\/en.wikipedia.org\/wiki\/JSON\">JSON<\/a> standard for data interchange derives from JavaScript notation (hence the name<code> JavaScript Object  Notation<\/code>) but it is language-independent. It serves much the same purpose as XML and has a similar expressive power as XML. Depending on the how you represent data in the two formats, a JSON representation <i>may<\/i> be shorter than one in XML (primarily due to closing tags on XML elements). JSON is in some ways less burdensome than XML, though, as aptly described in <a href=\"http:\/\/www.json.org\/xml.html\">JSON: The Fat-Free Alternative to XML<\/a>.<\/p>\n<p class=\"MsoNormal\">PowerShell (with the advent of V3) provides direct support for JSON with the <code>ConvertFrom-Json <\/code>and <code>ConvertTo-Json <\/code>cmdlets. So let&#8217;s convert some simple JSON to a PowerShell object. The fields are an excerpt from a .NET DateTime object, showing both simple properties and nested properties.<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>$dateObject = @\"<\/code><\/b><\/p>\n<p><b><code>{<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 Day:\u00a0 \t\t16,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 DayOfWeek:\u00a0 \t\t3,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 DayOfYear:\u00a0 \t\t16,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 Hour:\u00a0 \t\t15,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 Minute:\u00a0 \t\t56,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 Month:\u00a0 \t\t1,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 Second:\u00a0 \t\t58,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 Ticks:\u00a0 \t\t634939486185604791,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 TimeOfDay:\u00a0 \t\t{<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TotalDays:\u00a0 \t\t0.66456667221180554,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TotalHours:\u00a0 \t\t15.949600133083333,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TotalMilliseconds:\u00a0 \t\t57418560.479100004,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TotalMinutes:\u00a0 \t\t956.976007985,<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 TotalSeconds:\u00a0 \t\t57418.5604791<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 },<\/code><\/b><\/p>\n<p><b><code>\u00a0\u00a0\u00a0 Year:\u00a0 \t\t2013<\/code><\/b><\/p>\n<p><b><code><\/code><\/b><\/p>\n<p><b><code>}<\/code><\/b><\/p>\n<p><b><code>\"@ | ConvertFrom-Json<\/code><\/b><\/p>\n<p><b><code>$dateObject<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Day  \t\tMonth Year<\/code><\/p>\n<p><code>---  \t\t----- ----<\/code><\/p>\n<p><code> \t\t\u00a016\u00a0\u00a0\u00a0\u00a0 1 2013<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>$dateObject.Year<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>2013<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>$dateObject.TimeOfDay.TotalSeconds<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\t57418.5604791<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">If you want to try the same thing with live data instead of a string constant, you can take the output of <code>Get-Date<\/code> as a list of properties and convert it to JSON :<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Date| Select-Object -Property* | ConvertTo-Json<\/pre>\n<p class=\"MsoNormal\">Tack on the same <code>ConvertFrom-Json<\/code> as a final command in the sequence to mimic the earlier results (though in this case it is not terribly productive!):<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Date| Select-Object -Property* | ConvertTo-Json| ConvertFrom-Json <\/pre>\n<p class=\"MsoNormal\">To demonstrate something a bit more useful, the <code>Invoke-WebRequest<\/code> cmdlet fetches content of a web page or web service. Here you see successive steps to fetch a web response, unwrap its JSON content, and convert that to PowerShell objects so that you can directly address its elements. (Note that the actual JSON data-the output of the second command-was manually run through the <a href=\"http:\/\/jsonformatter.curiousconcept.com\/\">JSON Formatter and Validator<\/a> to pretty-print it for this article; otherwise you would see everything on one line, making it very difficult to see what is there.)<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code># Web response as a PowerShell object<br \/>\n \t\t$url = \"http:\/\/search.twitter.com\/search.json?q=PowerShell\"<\/code><\/b><\/p>\n<p><b><code>Invoke-WebRequest $url<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\tStatusCode\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : 200<\/code><\/p>\n<p><code> \t\tStatusDescription : OK<\/code><\/p>\n<p><code>Content\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: {\"completed_in\":0.037,\"max_id\":291699484801527808,<\/code><\/p>\n<p><code><\/code><\/p>\n<p><code>Forms\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: {}<\/code><\/p>\n<p><code>Headers\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: {[X-Transaction, 89f7072a7bd683a0], [X-Frame-Options, SAMEORIGIN]...}<\/code><\/p>\n<p><code>Images\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: {}<\/code><\/p>\n<p><code> \t\tInputFields\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : {}<\/code><\/p>\n<p><code>Links\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: {}<\/code><\/p>\n<p><code> \t\tParsedHtml\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 :  \t\tmshtml.HTMLDocumentClass<\/code><\/p>\n<p><code> \t\tRawContentLength\u00a0 : 11985<\/code><\/p>\n<p><code>. . .<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code># JSON response<br \/>\n \t\t(Invoke-WebRequest $url).content<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>{<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0 \"completed_in\":0.015,<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0 \"max_id\":291699484801527808,<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0 \"max_id_str\":\"291699484801527808\",<\/code><\/p>\n<p><code><\/code><\/p>\n<p><code> \t\t\u00a0\u00a0 \"page\":1,<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0 \"query\":\"PowerShell\",<\/code><\/p>\n<p><code><\/code><\/p>\n<p><code> \t\t\u00a0\u00a0 \"results\":[<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0 {<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\"created_at\":\"Thu, 17 Jan 2013  \t\t00:12:31 +0000\",<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"from_user\":\"mjolinor\",<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"from_user_id\":226782418,<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  \t\t\"from_user_id_str\":\"226782418\",<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"from_user_name\":\"Rob  \t\tCampbell\",<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \"geo\":null,<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 . . .<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code># JSON response converted to a PowerShell object<br \/>\n \t\t$jsonContent = (Invoke-WebRequest $url).content |\u00a0 ConvertFrom-Json<\/code><\/b><\/p>\n<p><b><code>$ jsonContent<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code> \t\tcompleted_in\u00a0\u00a0\u00a0\u00a0 : 0.023<\/code><\/p>\n<p><code>max_id\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: 291699484801527808<\/code><\/p>\n<p><code> \t\tmax_id_str\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : 291699484801527808<\/code><\/p>\n<p><code><\/code><\/p>\n<p><code>page\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: 1<\/code><\/p>\n<p><code>query\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: PowerShell<\/code><\/p>\n<p><code><\/code><\/p>\n<p><code>results\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\t: {@{created_at=Thu, 17 Jan 2013 00:12:31 +0000; from_user=mjolinor;<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  \t\t\u00a0\u00a0\u00a0\u00a0\u00a0from_user_id=226782418; from_user_id_str=226782418;<\/code><\/p>\n<p><code> \t\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \t\tfrom_user_name=Rob Campbell; geo=; ...<\/code><\/p>\n<p><code>. . .<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>$ jsonContent.completed_in<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>0.013<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code>$ jsonContent.results[0].created_at<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>Thu, 17  \t\tJan 2013 00:12:31 +0000<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">See <a href=\"http:\/\/www.json.org\/\">JSON.org<\/a> for more on JSON.<\/p>\n<h2>Excel<\/h2>\n<p class=\"MsoNormal\">Reading and writing Excel from PowerShell is done fairly easily as well, though it is much more involved than everything else you have read thusfar. Chances are your needs fall into one of two camps: reading Excel on a system that has Excel installed, and reading Excel on a system that does not (perhaps because your application needs to be used by all your customer service reps by their machines are not set up with Excel).<\/p>\n<h3>Excel with Excel<\/h3>\n<p class=\"MsoNormal\">To <i>read<\/i> Excel, Robert M. Toups, Jr. in his blog entry <a href=\"http:\/\/activelydirect.blogspot.com\/2011\/03\/speed-up-reading-excel-files-in.html\">Speed Up Reading Excel Files in PowerShell<\/a> explains that while loading a spreadsheet in PowerShell is fast, actually reading its cells is very slow. On the other hand, PowerShell can read a text file very quickly, so his solution is to load the spreadsheet in PowerShell, use Excel&#8217;s native CSV export process to save it as a CSV file, then use PowerShell&#8217;s standard <code>Import-Csv<\/code> cmdlet to process the data blazingly fast. He reports that this has given him up to a 20 times faster import process! Leveraging Toups&#8217; code, I created an <code>Import-Excel<\/code> function that lets you import spreadsheet data very easily:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b><code>$spreadsheetData = Import-Excel \"datadir\\sample.xlsx\"<\/code><\/b><\/p>\n<p><b><code>$spreadsheetData<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>name\u00a0\u00a0 \t\tid<\/code><\/p>\n<p><code>----\u00a0\u00a0 \t\t--<\/code><\/p>\n<p><code>foo\u00a0  \t\t\u00a0\u00a03\u00a0 <\/code><\/p>\n<p><code>bar\u00a0  \t\t\u00a0\u00a025 <\/code><\/p>\n<p><code>alpha \t\t\u00a0-99<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b><code># Display name in first row (0-based index)<\/code><\/b><\/p>\n<p><b><code>$spreadsheetData[0].name<\/code><\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><code>foo<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"MsoNormal\">My code adds the capability to select a <i> specific<\/i> worksheet within an Excel workbook, rather than just using the <i> default<\/i> worksheet (i.e. the active sheet at the time you saved the file). If you omit the -SheetName parameter, it uses the default worksheet.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function Import-Excel([string]$FilePath, [string]$SheetName = \"\")\r\n{\r\n\u00a0\u00a0\u00a0 $csvFile = Join-Path $env:temp (\"{0}.csv\" -f (Get-Item -path $FilePath).BaseName)\r\n\u00a0\u00a0\u00a0 if (Test-Path -path $csvFile) { Remove-Item -path $csvFile }\r\n\r\n\u00a0\u00a0\u00a0 # convert Excel file to CSV file\r\n\u00a0\u00a0\u00a0 $xlCSVType = 6 # SEE: http:\/\/msdn.microsoft.com\/en-us\/library\/bb241279.aspx\r\n\u00a0\u00a0\u00a0 $excelObject = New-Object -ComObject Excel.Application\u00a0 \r\n\u00a0\u00a0\u00a0 $excelObject.Visible = $false \r\n\u00a0\u00a0\u00a0 $workbookObject = $excelObject.Workbooks.Open($FilePath)\r\n\u00a0\u00a0\u00a0 SetActiveSheet $workbookObject $SheetName | Out-Null\r\n\u00a0\u00a0\u00a0 $workbookObject.SaveAs($csvFile,$xlCSVType) \r\n\u00a0\u00a0\u00a0 $workbookObject.Saved = $true\r\n\u00a0\u00a0\u00a0 $workbookObject.Close()\r\n\r\n\u00a0\u00a0\u00a0\u00a0 # cleanup \r\n\u00a0\u00a0\u00a0 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbookObject) |\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Out-Null\r\n\u00a0\u00a0\u00a0 $excelObject.Quit()\r\n\u00a0\u00a0\u00a0 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelObject) |\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0Out-Null\r\n\u00a0\u00a0\u00a0 [System.GC]::Collect()\r\n\u00a0\u00a0\u00a0 [System.GC]::WaitForPendingFinalizers()\r\n\r\n\u00a0\u00a0\u00a0 # now import and return the data \r\n\u00a0\u00a0\u00a0 Import-Csv -path $csvFile\r\n}\r\n<\/pre>\n<p>These supplemental functions are used by <code>Import-Excel<\/code>:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function FindSheet([Object]$workbook, [string]$name)\r\n{\r\n\u00a0\u00a0\u00a0 $sheetNumber = 0\r\n\u00a0\u00a0\u00a0 for ($i=1; $i -le $workbook.Sheets.Count; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ($name -eq $workbook.Sheets.Item($i).Name) { $sheetNumber = $i; break }\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 return $sheetNumber\r\n}\r\n\r\nfunction SetActiveSheet([Object]$workbook, [string]$name)\r\n{\r\n\u00a0\u00a0\u00a0 if (!$name) { return }\r\n\u00a0\u00a0\u00a0 $sheetNumber = FindSheet $workbook $name\r\n\u00a0\u00a0\u00a0 if ($sheetNumber -gt 0) { $workbook.Worksheets.Item($sheetNumber).Activate() }\r\n\u00a0\u00a0\u00a0 return ($sheetNumber -gt 0)\r\n}\r\n\r\n\r\n<\/pre>\n<p class=\"MsoNormal\">To <i>write<\/i> to Excel, Robert M. Toups, Jr. in his blog entry <a href=\"http:\/\/activelydirect.blogspot.com\/2011\/03\/write-excel-spreadsheets-fast-in.html\">Write Excel Spreadsheets Fast in PowerShell<\/a> suggests that if you have a lot of data to load into Excel, doing this directly in PowerShell is much more time-consuming than converting the data to a CSV file than letting Excel&#8217;s native CSV import process-controlled through PowerShell-do the data loading. I adapted his code to be suitable for a generic Excel exporter, allowing you to specify the title and author of the Excel workbook, and the name of the single worksheet it creates in the file:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function Export-Excel([string]$FilePath, [string]$Title, [string]$Author, [string]$SheetName, [Object[]]$Data)\r\n{\r\n\u00a0\u00a0\u00a0 # Specify to save in a standard .XSLX format.\r\n\u00a0\u00a0 \u00a0$xlOpenXMLType = 51 # SEE: http:\/\/msdn.microsoft.com\/en-us\/library\/bb241279.aspx\r\n\r\n\u00a0\u00a0\u00a0 $csvFile = Join-Path $env:temp `\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 (\"{0}.csv\" -f ([System.IO.FileInfo]$FilePath).BaseName)\r\n\u00a0\u00a0\u00a0 if (Test-Path -path $csvFile) { Remove-Item -path $csvFile }\r\n\u00a0\u00a0\u00a0 if (Test-Path -path $FilePath) { Remove-Item -path $FilePath }\r\n\r\n\u00a0\u00a0\u00a0 $Data | Export-Csv -path $csvFile -noTypeInformation\r\n\u00a0\r\n\u00a0\u00a0\u00a0 $excelObject = New-Object -comObject Excel.Application\r\n\u00a0\u00a0\u00a0 $excelObject.Visible = $false \r\n\u00a0\u00a0\u00a0 $workbookObject = $excelObject.Workbooks.Open($csvFile)\r\n\u00a0\u00a0\u00a0 $workbookObject.Title = $Title\r\n\u00a0\u00a0\u00a0 $workbookObject.Author = $Author\r\n\u00a0\u00a0\u00a0 $worksheetObject = $workbookObject.Worksheets.Item(1)\r\n\u00a0\u00a0\u00a0 $worksheetObject.UsedRange.Columns.Autofit() | Out-Null\r\n\u00a0\u00a0\u00a0 $worksheetObject.Name = $SheetName\r\n\u00a0\u00a0\u00a0 $workbookObject.SaveAs($FilePath, $xlOpenXMLType)\r\n\u00a0\u00a0\u00a0 $workbookObject.Saved = $true\r\n\u00a0\u00a0\u00a0 $workbookObject.Close()\r\n\r\n\u00a0\u00a0\u00a0 # cleanup\r\n\u00a0\u00a0\u00a0 if (Test-Path -path $csvFile) { Remove-Item -path $csvFile }\r\n\u00a0\u00a0\u00a0 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbookObject) |\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Out-Null\r\n\u00a0\u00a0\u00a0 $excelObject.Quit()\r\n\u00a0\u00a0\u00a0 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelObject) |\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Out-Null\r\n\u00a0\u00a0\u00a0 [System.GC]::Collect()\r\n\u00a0\u00a0\u00a0 [System.GC]::WaitForPendingFinalizers()\r\n}\r\n\u00a0\r\n<\/pre>\n<p class=\"MsoNormal\">Of course there is so much more customization you could do when creating an Excel file. The MSDN documentation for the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms262200(v=office.14).aspx\"> Microsoft.Office.Interop.Excel<\/a> namespace is your starting point for digging into the wealth of methods and properties available on an <code>Excel.Application<\/code> object. For completeness, I will also mention Jeff Hicks&#8217; <a href=\"http:\/\/www.petri.co.il\/export-to-excel-with-powershell.htm\">Integrating Microsoft Excel with PowerShell<\/a> article that shows how to do direct access to worksheet cells, rather than going through the intermediate CSV steps discussed above. It is quite suitable when you do not have a lot of data to process.<\/p>\n<h3>Excel without Excel<\/h3>\n<p class=\"MsoNormal\">There are certainly situations where you might want to read an Excel spreadsheet on a machine that does not have Excel installed. In this case you must make do without the automation capabilities of Office, instead opting for a data access approach using <a href=\"http:\/\/en.wikipedia.org\/wiki\/OLE_DB\">OLEDB<\/a> or <a href=\"http:\/\/en.wikipedia.org\/wiki\/ODBC\">ODBC<\/a>. A very clean approach to this scenario using OLEDB is the publically available <a href=\"http:\/\/sev17.com\/2010\/03\/01\/querying-oracle-from-powershell-part-2\">Get-OLEDBData<\/a> created by Chad Miller, avid PowerShell aficionado. Here is the entire function:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function Get-OLEDBData ($connectstring, $sql) {\r\n\u00a0\u00a0 $OLEDBConn = New-Object System.Data.OleDb.OleDbConnection($connectstring)\r\n\u00a0\u00a0 $OLEDBConn.open()\r\n\u00a0\u00a0 $readcmd = New-Object system.Data.OleDb.OleDbCommand($sql,$OLEDBConn)\r\n\u00a0\u00a0 $readcmd.CommandTimeout = '300'\r\n\u00a0\u00a0 $da = New-Object system.Data.OleDb.OleDbDataAdapter($readcmd)\r\n\u00a0\u00a0 $dt = New-Object system.Data.datatable\r\n\u00a0\u00a0 [void]$da.fill($dt)\r\n\u00a0\u00a0 $OLEDBConn.close()\r\n\u00a0\u00a0 return $dt\r\n} \r\n<\/pre>\n<p class=\"MsoNormal\">This function returns a <code>DataTable<\/code>, one of the object types readily handled by PowerShell. The catch, of course, is that you need to know what to provide for the $connectstring and $sql parameters. The commentary of Miller&#8217;s code in the PowerShell Code Repository details connection strings for commonly used data sources: Excel 2007 (or higher), Excel 2003, Informix, Oracle, and SQL Server. From there, you can see that Excel should use this:<\/p>\n<pre>Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\"C:\\path\\to\\your\\file.xlsx\";Extended Properties=\"Excel 12.0 Xml;HDR=YES\"<\/pre>\n<p class=\"MsoNormal\">But to understand what really goes into an Excel connection string, I borrowed this format from the PowerShell Scripting Guy in his post <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2008\/09\/11\/how-can-i-read-from-excel-without-using-excel.aspx\"> How Can I Read from Excel Without Using Excel?<\/a> Breaking it out this way makes things immediately obvious:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$strFileName\u00a0\u00a0\u00a0\u00a0\u00a0 = \"C:\\usr\\tmp\\sample.xlsx\"\r\n$strProvider\u00a0\u00a0\u00a0\u00a0\u00a0 = \"Provider=Microsoft.ACE.OLEDB.12.0\"\r\n$strDataSource\u00a0\u00a0\u00a0 = \"Data Source=`\"$strFileName`\"\"\r\n$strExtend\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = \"Extended Properties=`\"Excel 12.0 Xml;HDR=YES`\"\"\r\n$connectionString = \"$strProvider;$strDataSource;$strExtend\"\r\n\r\n$strSheetName\u00a0\u00a0\u00a0\u00a0 = 'Sheet1'\r\n$strQuery\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = \"Select * from [$strSheetName$]\"\r\n\r\nGet-OLEDBData -connectstring $connectionString -sql $strQuery \r\n<\/pre>\n<p class=\"MsoNormal\">One caveat: on a 64-bit machine you can install either 32-bit Office or 64-bit Office. (The latter provides additional capacity-for example, handling spreadsheets larger than 2GB-but at a potential cost of compatibility with add-ins not yet having 64-bit versions.) If you have installed 32-bit Office you will then need to run 32-bit PowerShell to use the Microsoft.ACE.OLEDB.12.0 or <span class=\"co1\">Microsoft.Jet.OLEDB.4.0 provider. If you attempt to use 64-bit PowerShell you will get an error stating: <i>The &#8216;Microsoft.ACE.OLEDB.12.0&#8217; provider is not registered on the local machine. <\/i>While not technically a bug, this is a known issue<i>\u00a0<\/i><i><\/i>and <\/span> <a href=\"https:\/\/social.msdn.microsoft.com\/Forums\/en-US\/1d5c04c7-157f-4955-a14b-41d912d50a64\/how-to-fix-error-the-microsoftaceoledb120-provider-is-not-registered-on-the-local-machine?forum=vstsdb\">this post<\/a><span class=\"co1\"> on the MSDN forums shows one developer&#8217;s journey to find a workaround.<\/span><\/p>\n<h2>More to Come&#8230;<\/h2>\n<p class=\"MsoNormal\">This series continues with Part 2 on XML data<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>One of the frustrations of anyone beginning with PowerShell is the simple task of getting data in and out. To help out with this, Michael Sorens begins a series that shows you how to import data from most of the common file formats and sources. He also shows how to export data in a range of formats.&hellip;<\/p>\n","protected":false},"author":221868,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[35],"tags":[4242,4635,4179,4871],"coauthors":[6802],"class_list":["post-1729","post","type-post","status-publish","format-standard","hentry","category-powershell","tag-basics","tag-powershell","tag-source-control","tag-sysadmin"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1729","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221868"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1729"}],"version-history":[{"count":9,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1729\/revisions"}],"predecessor-version":[{"id":75034,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1729\/revisions\/75034"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1729"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1729"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1729"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1729"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}