<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://powershell.com/cs/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title type="html">Dreaming in PowerShell</title><subtitle type="html" /><id>http://powershell.com/cs/blogs/tobias/atom.aspx</id><link rel="alternate" type="text/html" href="http://powershell.com/cs/blogs/tobias/default.aspx" /><link rel="self" type="application/atom+xml" href="http://powershell.com/cs/blogs/tobias/atom.aspx" /><generator uri="http://communityserver.org" version="4.1.30929.2835">Community Server</generator><updated>2011-05-23T14:57:00Z</updated><entry><title>Test-Driving PowerShell v3: How To Install And Run PSv3 BETA</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2012/05/15/test-driving-powershell-v3-how-to-install-and-run-ctpv2.aspx" /><id>/cs/blogs/tobias/archive/2012/05/15/test-driving-powershell-v3-how-to-install-and-run-ctpv2.aspx</id><published>2012-05-15T02:32:00Z</published><updated>2012-05-15T02:32:00Z</updated><content type="html">&lt;p&gt;A&amp;nbsp;new PowerShell version is coming soon! The language itself will stay pretty much the same, but &lt;strong&gt;there are tons of cool new features&lt;/strong&gt; that &lt;strong&gt;enhance performance&lt;/strong&gt; and &lt;strong&gt;ease of use&lt;/strong&gt;. A complete list of all of the new features is available here: &lt;a href="http://www.microsoft.com/en-us/download/details.aspx?id=28998"&gt;http://www.microsoft.com/en-us/download/details.aspx?id=28998&lt;/a&gt;&amp;nbsp;(see the lower part of this page for feature details).&lt;/p&gt;
&lt;p&gt;Which raises the question: &lt;strong&gt;how do I install and get PSv3 up and running&lt;/strong&gt;? And are there &lt;strong&gt;any compatibility issues&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update: PowerShell V3 Beta is out. I updated all links accordingly.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Requirements for PowerShell V3 Beta&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s first take a look at the &lt;strong&gt;prerequisites&lt;/strong&gt;. Before you can &lt;strong&gt;install PowerShell V3 Beta&lt;/strong&gt;, you must ensure that these prerequisites are met:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you run Windows Server 2008SP2, Windows7SP1/Server 2008R2SP1 (so with all of the latest service packs installed). &lt;strong&gt;Windows XP/Server 2003/Vista is not supported&lt;/strong&gt; and will not be supported anymore apparently.&lt;/li&gt;
&lt;li&gt;you must have installed &lt;strong&gt;.NET Framework 4.0&lt;/strong&gt;. If you are unsure, download the .NET Framework 4.0 runtimes (&lt;a href="http://www.microsoft.com/en-us/download/details.aspx?id=17718"&gt;http://www.microsoft.com/en-us/download/details.aspx?id=17718&lt;/a&gt;)&amp;nbsp;and try and install them. If the installer offers to &amp;quot;repair&amp;quot; the installation, then you know .NET 4.0 is present, and you can cancel the installation. &lt;/li&gt;
&lt;li&gt;you &lt;strong&gt;must use an en-US MUI&lt;/strong&gt;, so CTP2 does not run on Windows if any other language is installed (see below for some workarounds)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Installing English MUI&lt;/h3&gt;
&lt;p&gt;Unfortunately,&amp;nbsp;PowerShell V3 Beta &lt;strong&gt;only installs on US-English systems&lt;/strong&gt;. If you are&amp;nbsp;running Windows 7 Business, Enterprise or Ultimate, you can temporarily change your display language to En-US, install&amp;nbsp;the Beta, then change back to whatever language you prefer.&amp;nbsp;PowerShell V3 Beta runs on any locale, it just installs only on En-US.&lt;/p&gt;
&lt;p&gt;But what if you do not have access to the En-US language pack? And what if you run Windows 7 Professional or a Vista version that does not support MUIs?&lt;/p&gt;
&lt;p&gt;You can &lt;strong&gt;download the&amp;nbsp;MUI packs separately&lt;/strong&gt; for free. Just make sure you download the right MUI. It must match your operating system and your platform (32bit/64bit) and your service pack level. You can find all of the downloads&amp;nbsp;on various sites including this one: &lt;a href="http://www.mydigitallife.info/windows-7-sp1-mui-language-packs-official-direct-download-links/"&gt;http://www.mydigitallife.info/windows-7-sp1-mui-language-packs-official-direct-download-links/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On a windows system that supports MUIs, after installing the downloaded MUI, it will show up in your control panel regions applet when you select &amp;nbsp;&lt;strong&gt;Change Display Language&lt;/strong&gt;. If you are running a windows version that does not support MUIs, go&amp;nbsp;to &lt;a href="http://www.froggie.sk/"&gt;http://www.froggie.sk/&lt;/a&gt;&amp;nbsp;and download the &lt;strong&gt;free tool vistalizator&lt;/strong&gt;. It &lt;strong&gt;allows you to open and install downloaded MUIs and then change your display language&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Installing PowerShell V3 Beta&lt;/h3&gt;
&lt;p&gt;Once the prerequisites are met and your display language is switched to En-US, you can download and install the Beta package that you downloaded from here:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="http://www.microsoft.com/en-us/download/details.aspx?id=28998"&gt;http://www.microsoft.com/en-us/download/details.aspx?id=28998&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If the installation package complains that it is not applicable to your system, then you did not meet all of the prerequisites mentioned, or you downloaded the wrong version (wrong OS or platform).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The installation requires a reboot, and after rebooting, PowerShell V3 Beta is up and running. You can now enjoy the many new features, including &lt;strong&gt;tabcompletion in the console that no longer erases everything to the right&lt;/strong&gt;, and a drastically &lt;strong&gt;improved ISE editor&lt;/strong&gt;. Just note that the first launch of powershell.exe and ISE may take some time.&lt;/p&gt;
&lt;h3&gt;Getting Updated Help&lt;/h3&gt;
&lt;p&gt;PowerShell V3 no longer ships with help files, so one of the first things you may want to do is launch a powershell with full admin privileges, and then &lt;strong&gt;download the help materials&lt;/strong&gt; like so:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;nbsp;update-help -UICulture en-us -force&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Preserving Compatibility&lt;/h3&gt;
&lt;p&gt;PowerShell V3 Beta is mostly backwards compatible, so you can continue to run your old scripts. When you create new scripts, though, you may be lured into using new PSv3 features, and if you do, your scripts no longer run on PSv2. To check, you can always &lt;strong&gt;run a test environment in PowerShell V2 mode&lt;/strong&gt; like this:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; powershell.exe -version 2.0&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This will launch a new PowerShell session in 2.0 mode, so you can test-drive your scripts and ensure they do not use V3 features and fail in V2.&lt;/p&gt;
&lt;p&gt;We&amp;#39;ll begin covering new PowerShell V3 features&amp;nbsp;soon so you&amp;#39;ll be prepared when PowerShell V3 hits the floor later this year. With the installation guideline found here, you now have all the details you need to setup your own cozy PSv3 test lab. Just remember: &lt;em&gt;the current V3 is a Beta, it is not a production environment. While I personally did not stumble across any buggy behavior, you may not want to toss this version onto your production servers yet&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;Stay tuned and have fun with PSv3 now!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=16537" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="CTP2" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/CTP2/default.aspx" /><category term="PowerShell V2" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/PowerShell+V2/default.aspx" /><category term="V3" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/V3/default.aspx" /><category term="vistalizator" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/vistalizator/default.aspx" /><category term="MUI" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/MUI/default.aspx" /><category term="Install" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Install/default.aspx" /></entry><entry><title>Managing Child Processes</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2012/05/09/managing-child-processes.aspx" /><id>/cs/blogs/tobias/archive/2012/05/09/managing-child-processes.aspx</id><published>2012-05-09T02:09:00Z</published><updated>2012-05-09T02:09:00Z</updated><content type="html">&lt;p&gt;Today in my training, a student asked &lt;strong&gt;how to manage child processes&lt;/strong&gt;. That&amp;#39;s not a bad question at all, and as it turns out, it&amp;#39;s a &lt;strong&gt;great way to show off the capabilities of PowerShell&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So why would you care about child processes in the first place? Well, for example to monitor and keep track of what installation packages are doing.&lt;/p&gt;
&lt;p&gt;When you launch an application from powershell, like &lt;strong&gt;setup.exe&lt;/strong&gt; for example, you can get back its process id and monitor it or kill it if it takes too long&amp;nbsp;- that&amp;#39;s cool. But what if this process launches additional processes in the background? How can you get a hold of all of these processes?&lt;/p&gt;
&lt;h3&gt;Launching A Process And Keeping Control&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s start simple and try and &lt;strong&gt;launch a process from PowerShell&lt;/strong&gt;. The objective is to &lt;strong&gt;get back its process id&lt;/strong&gt;, so you can continue to check and monitor it. &lt;strong&gt;Start-Process&lt;/strong&gt; can start processes for you, and with a little-known parameter called &lt;strong&gt;-PassThru&lt;/strong&gt;, it will even pass the process object back to you. This way, you can store it in a variable and use it later to retrieve the process id, read its CPU consumption or kill it:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$process&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Start-Process&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-FilePath&lt;/span&gt;&lt;/span&gt; notepad &lt;span style="color:#5f9ea0;"&gt;&lt;span class="modifier"&gt;-PassThru&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;PS&amp;gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$process&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;CPU&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;0,0156001&lt;br /&gt;PS&amp;gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$process&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;ID&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;3992&lt;br /&gt;PS&amp;gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$process&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;hasExited&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;False&lt;br /&gt;PS&amp;gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$process&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Kill&lt;/span&gt;&lt;/span&gt;()&lt;br /&gt;PS&amp;gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$process&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;hasExited&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;True&lt;/div&gt;
&lt;p&gt;As you see, &lt;strong&gt;Start-Process&lt;/strong&gt; delivered back an object that was able to read real-time statistics from the live process and was even able to kill it at your convenience.&lt;/p&gt;
&lt;h3&gt;Finding Child Processes&lt;/h3&gt;
&lt;p&gt;To find all the processes a given process has started, &lt;strong&gt;Get-Process&lt;/strong&gt; is of no use because it does not return the parent process id. Fortunately, &lt;strong&gt;WMI&lt;/strong&gt; can help. Its class &lt;strong&gt;Win32_Process&lt;/strong&gt; does include the information. So here is a small function called &lt;strong&gt;Find-ChildProcess&lt;/strong&gt;. It accepts a process id and returns all the child processes that were spawned from the parent process:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Find-ChildProcess&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$ID&lt;/span&gt;&lt;/span&gt;&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$PID&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WmiObject&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Class&lt;/span&gt;&lt;/span&gt; Win32_Process &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;Filter&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;ParentProcessID=$ID&amp;quot;&lt;/span&gt;&lt;/span&gt; |&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Property&lt;/span&gt;&lt;/span&gt; ProcessName, ProcessID, CommandLine&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;By default, &lt;strong&gt;Find-ChildProcess&lt;/strong&gt; uses the process ID of your current PowerShell host. So if you run &lt;strong&gt;Find-ChildProcess&lt;/strong&gt; without specifying a process ID, you get back all the processes that you launched from within your powershell session. Try it and launch a couple of windowed applications such as notepad! Then, call &lt;strong&gt;Find-ChildProcess&lt;/strong&gt;. You get back all the child processes. Cool!&lt;/p&gt;
&lt;h3&gt;Making It Recursive&lt;/h3&gt;
&lt;p&gt;Of course, a child process can launch another process as well, so there&amp;nbsp;can be a literal &lt;strong&gt;process tree&lt;/strong&gt;. To get a hold of these child-child-processes, what we need as a final touch is &lt;strong&gt;recursion&lt;/strong&gt;. Since we already have a function that gets child processes, all you need to do&amp;nbsp;is &lt;strong&gt;call it again&lt;/strong&gt; for all child processes to find the processes of a child process.&lt;/p&gt;
&lt;p&gt;The final solution also&amp;nbsp;renames and changes the column (property) &lt;strong&gt;ProcessID&lt;/strong&gt;. It&amp;nbsp;renames it to &amp;#39;ID&amp;#39; and changes the type to an integer array. Why? Because &lt;strong&gt;Stop-Process&lt;/strong&gt; binds to that property via pipeline. So by&amp;nbsp;fitting the column to the needs of &lt;strong&gt;Stop-Process&lt;/strong&gt;, you can then pipe the results of &lt;strong&gt;Find-ChildProcess&lt;/strong&gt; directly to &lt;strong&gt;Stop-Process&lt;/strong&gt; in case you need to stop and kill the whole enchilada (you can &lt;strong&gt;download&lt;/strong&gt; this function here: &lt;a href="http://powershell.com/cs/media/p/16455.aspx"&gt;http://powershell.com/cs/media/p/16455.aspx&lt;/a&gt;):&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Find-ChildProcess&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$ID&lt;/span&gt;&lt;/span&gt;&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$PID&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$CustomColumnID&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; @{&lt;br /&gt;Name &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800000;"&gt;&lt;span class="string"&gt;&amp;#39;Id&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Expression &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; { [&lt;span class="datatype"&gt;&lt;span style="color:#0000ff;"&gt;Int&lt;/span&gt;&lt;/span&gt;[]]&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;ProcessID&lt;/span&gt;&lt;/span&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$result&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WmiObject&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Class&lt;/span&gt;&lt;/span&gt; Win32_Process &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;Filter&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;ParentProcessID=$ID&amp;quot;&lt;/span&gt;&lt;/span&gt; |&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Property&lt;/span&gt;&lt;/span&gt; ProcessName, &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$CustomColumnID&lt;/span&gt;&lt;/span&gt;, CommandLine&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$result&lt;/span&gt;&lt;br /&gt;&lt;span class="var"&gt;$result&lt;/span&gt;&lt;/span&gt; &lt;strong&gt;| Where-Object { $_.ID -ne $null }&lt;/strong&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;ForEach-Object&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Find-ChildProcess&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-id&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;Id&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; &lt;em&gt;I added the &lt;strong&gt;Where-Object&lt;/strong&gt; in black and bold above after it occured to me that some process do not have parent process information and this could lead to unwanted WMI &amp;quot;query invalid&amp;quot; exceptions. Now all is fine.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;To &lt;strong&gt;test drive&lt;/strong&gt; this, in your powershell enter &lt;strong&gt;Start-Process powershell&lt;/strong&gt;. This opens&lt;/em&gt; a new powershell in a new window - a child process. Inside the new powershell window, enter &lt;strong&gt;Start-Process cmd&lt;/strong&gt;. This opens yet another nested child process, a cmd shell. Inside of it, enter &lt;strong&gt;notepad&lt;/strong&gt; to launch an instance of notepad.&lt;/p&gt;
&lt;p&gt;Then return to your initial powershell and display the child items:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Find-ChildProcess&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;ProcessName Id CommandLine &lt;br /&gt;&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-----------&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;--&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-----------&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;notepad.exe&lt;/span&gt;&lt;/span&gt; 3180 &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;C:\Windows\system32\notepad.exe&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;powershell.exe&lt;/span&gt;&lt;/span&gt; 6112 &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;cmd.exe&lt;/span&gt;&lt;/span&gt; 5728 &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;C:\Windows\system32\cmd.exe&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;notepad.exe&lt;/span&gt;&lt;/span&gt; 5636 notepad&lt;/div&gt;
&lt;p&gt;As you see, all child processes - even the nested ones - are reported. &lt;/p&gt;
&lt;p&gt;And since there is a fitting column ID with the proper type, you can then pipe the results on to &lt;strong&gt;Stop-Process&lt;/strong&gt; to kill the entire process tree:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Find-ChildProcess&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Stop-Process&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;So &lt;strong&gt;Find-ChildProcess&lt;/strong&gt; effectively became a new member of the PowerShell command family, playing by the rules. You can combine it with other related cmdlets. Great question! And a cool solution that &lt;strong&gt;illustrates the power of PowerShell and its extensibility&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Stay tuned...!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=16456" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Recurse" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Recurse/default.aspx" /><category term="Childprocess" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Childprocess/default.aspx" /><category term="Process" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Process/default.aspx" /><category term="Win32_Process" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Win32_5F00_Process/default.aspx" /><category term="Child" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Child/default.aspx" /></entry><entry><title>Don't Waste Speed - How To Activate The PowerShell Turbo Mode</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2012/05/01/don-t-waste-speed-how-to-activate-the-powershell-turbo-mode.aspx" /><id>/cs/blogs/tobias/archive/2012/05/01/don-t-waste-speed-how-to-activate-the-powershell-turbo-mode.aspx</id><published>2012-05-01T20:35:00Z</published><updated>2012-05-01T20:35:00Z</updated><content type="html">&lt;p&gt;This afternoon, my buddy Alexandar (and fellow PowerShell MVP from Serbia) reviewed another batch of PowerTips when he stumbled across some lines of code that looked a bit unintuitive to him and asked to rephrase them. Sometimes there is more than one way to Rome. There is nothing bad about having choices. Today &lt;strong&gt;I&amp;#39;d like to show how to pick the fastest way&lt;/strong&gt; to Rome - and how you can speed up your PowerShell code tremendously by knowing about some easy design rules.&lt;/p&gt;
&lt;h3&gt;Understanding&amp;nbsp;The PowerShell Pipeline&lt;/h3&gt;
&lt;p&gt;The PowerShell pipeline is a great feature but&amp;nbsp;many users&amp;nbsp;aren&amp;#39;t aware that the &lt;strong&gt;pipeline actually is a throttling mechanism&lt;/strong&gt; designed to &lt;strong&gt;minimize memory consumption&lt;/strong&gt;. When you read in a large file, for example, by reading this file line-by-line and processing each line over the pipeline, only one line at a time needs to be stored in memory. That&amp;#39;s why PowerShell can easily read and process huge log files.&lt;/p&gt;
&lt;p&gt;The backside is that &lt;strong&gt;speed is fueled by memory and CPU&lt;/strong&gt;. The memory throttling done by the PowerShell &lt;strong&gt;pipeline takes away memory&lt;/strong&gt; and &lt;strong&gt;reduces speed&lt;/strong&gt;. And the results can be significant. Have a look:&lt;/p&gt;
&lt;p&gt;PS&amp;gt; 1..100 | Get-Random&lt;br /&gt;66&lt;br /&gt;PS&amp;gt; 1..10000 | Get-Random&lt;br /&gt;9746&lt;br /&gt;PS&amp;gt; 1..1000000 | Get-Random&lt;br /&gt;509967&lt;/p&gt;
&lt;p&gt;All of these lines generate a range of numbers and then pick one random number from it. When you try this for yourself, you&amp;#39;ll notice that the first two commands run almost instantaneously. The third line however takes forever. The overhead produced by the PowerShell pipeline accumulates with each element that needs to travel across it. So in the last command, one million numbers need to travel one by one to &lt;strong&gt;Get-Random&lt;/strong&gt;, and that takes a lot of time.&lt;/p&gt;
&lt;p&gt;In many scenarios, the great &lt;strong&gt;memory-saving aspect&lt;/strong&gt; of the PowerShell pipeline is not important at all. In our current example, it definitely does not matter. So here you can speed up things easily by avoiding the pipeline and passing the data directly to the parameter that would else receive it over the pipeline:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Random&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-InputObject&lt;/span&gt;&lt;/span&gt; (1..1000000)&lt;br /&gt;770786&lt;/div&gt;
&lt;p&gt;Notice how the very same operation takes only a fraction of a second instead of many seconds.&lt;/p&gt;
&lt;h3&gt;Every Day Scenarios To Save Time&lt;/h3&gt;
&lt;p&gt;There are tons of every day scenarios that you can speed up this way. Let&amp;#39;s start with a very common one: &lt;strong&gt;discarding data&lt;/strong&gt;. When you do not need data, you have the choice of piping it to &lt;strong&gt;Out-Nu&lt;/strong&gt;ll or assigning it to the special variable &lt;strong&gt;$null&lt;/strong&gt;. Guess which way is faster? Test for yourself:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Measure-Command&lt;/span&gt;&lt;/span&gt; {1..100000 | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-Null&lt;/span&gt;&lt;/span&gt; }).&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;TotalMilliseconds&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;1114,3006 &lt;span style="color:#008000;"&gt;&lt;span class="comment"&gt;#!!!!!!!!!!! Over 1 f...ing second !!!!!!!!!!)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;PS&amp;gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Measure-Command&lt;/span&gt;&lt;/span&gt; {&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$null&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; 1..100000 }).&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;TotalMilliseconds&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;29,3192&lt;br /&gt;PS&amp;gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Measure-Command&lt;/span&gt;&lt;/span&gt; {1..100000 &amp;gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$null&lt;/span&gt;&lt;/span&gt; }).&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;TotalMilliseconds&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;38,1542&lt;br /&gt;PS&amp;gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Measure-Command&lt;/span&gt;&lt;/span&gt; {[&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;void&lt;/span&gt;&lt;/span&gt;](1..100000) }).&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;TotalMilliseconds&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;28,2378&lt;/div&gt;
&lt;p&gt;All of these lines do the same: they dump data. The first approach uses the pipeline and takes &lt;strong&gt;over a second&lt;/strong&gt;! Assigning the data to &lt;strong&gt;$null&lt;/strong&gt; just takes lightning &lt;strong&gt;fast 30ms&lt;/strong&gt;. Holy Moly.&amp;nbsp;Of course the performance difference depends on how much data you need to dump. Often, it is much less than 100000 elements, so the performance gain becomes less impressive. However, just imagine this statement as part of a &lt;strong&gt;loop&lt;/strong&gt; that runs a number of times. Each time the loop runs, the &lt;strong&gt;peformance penalty sums up&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here is yet another common scenario: a loop. Some people use the pipeline to create loops:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; 1..100000 | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;ForEach-Object&lt;/span&gt;&lt;/span&gt; { &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;looping for the $_. time&amp;quot;&lt;/span&gt;&lt;/span&gt; }&lt;/div&gt;
&lt;p&gt;On my machine, it takes approximately &lt;strong&gt;7 seconds&lt;/strong&gt; (on yours it can be much faster, the point is just the relative comparison).&lt;/p&gt;
&lt;p&gt;The same loop can also be written without a pipeline, like this:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; for (&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$x&lt;/span&gt;&lt;/span&gt;&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt;1; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$x&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-le&lt;/span&gt;&lt;/span&gt; 100000; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$x&lt;/span&gt;&lt;/span&gt;&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;++&lt;/span&gt;&lt;/span&gt;) {&lt;br /&gt;&lt;span style="color:#800000;"&gt;&lt;span class="string"&gt;&amp;quot;looping for the $x. time&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;/div&gt;
&lt;p&gt;This one takes just 0,5 seconds. That&amp;#39;s a heck of a noticable speed increase.&lt;/p&gt;
&lt;h3&gt;The Pipeline Is EveryWhere&lt;/h3&gt;
&lt;p&gt;Sometimes, it is not so apparent that PowerShell is using the Pipeline when it really does. Here is some typical code:&lt;/p&gt;
&lt;p&gt;PS&amp;gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Measure-Command&lt;/span&gt;&lt;/span&gt; {&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Content&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$env:windir&lt;/span&gt;&lt;/span&gt;\&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;windowsupdate.log&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Foreach-Object&lt;/span&gt;&lt;/span&gt; { &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;reading line $_&amp;quot;&lt;/span&gt;&lt;/span&gt; }&lt;/p&gt;
&lt;p&gt;}).&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;TotalSeconds&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;1.2655871&lt;/p&gt;
&lt;p&gt;Compare this to the version that avoids the pipeline:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Measure-Command&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$all&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Content&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$env:windir&lt;/span&gt;&lt;/span&gt;\&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;windowsupdate.log&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-ReadCount&lt;/span&gt;&lt;/span&gt; 0&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;Foreach&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$line&lt;/span&gt;&lt;/span&gt; &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;in&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$all&lt;/span&gt;&lt;/span&gt;) { &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;reading line $line&amp;quot;&lt;/span&gt;&lt;/span&gt; }&lt;br /&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;}).&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;TotalSeconds&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;0.0848887&lt;/p&gt;
&lt;p&gt;Again, &lt;strong&gt;more than 14 times faster&lt;/strong&gt;. The thing to watch out here is to use &lt;strong&gt;Get-Content&lt;/strong&gt; with the parameter&lt;strong&gt; -ReadCount 0&lt;/strong&gt;. Without it, Get-Content by default is optimized for the pipeline and emits each line as a single object, causing again a lot of work for the pipeline.&lt;/p&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;The PowerShell &lt;strong&gt;pipeline is a great feature&lt;/strong&gt;! In the last example, thanks to the pipeline, just one line of a file needed to be stored in memory. The faster version without the pipeline, in contrast, needed to hold the entire text file in memory (variable &lt;strong&gt;$all&lt;/strong&gt;). So this article is not about avoiding the pipeline. It is about &lt;strong&gt;using the PowerShell resources wisely&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You have seen that there is a &lt;strong&gt;tradeoff: speed or small memory footprint&lt;/strong&gt;. Use the pipeline if you must conserve memory. Avoid the pipeline if you need speed. And take a look again at some of the initial examples: there are a lot of standard code scenarios where conserving memory really isn&amp;#39;t an issue. So by not using the Pipeline here, you can improve performance easily.&lt;/p&gt;
&lt;p&gt;Stay tuned...!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=16311" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Get-Content" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Get-Content/default.aspx" /><category term="Get-Random" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Get-Random/default.aspx" /><category term="$null" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/_2400_null/default.aspx" /><category term="Out-Null" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Out-Null/default.aspx" /></entry><entry><title>Displaying Data in Excel (and why it won't work)</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2012/04/24/displaying-data-in-excel-and-why-it-won-t-work.aspx" /><id>/cs/blogs/tobias/archive/2012/04/24/displaying-data-in-excel-and-why-it-won-t-work.aspx</id><published>2012-04-23T22:46:00Z</published><updated>2012-04-23T22:46:00Z</updated><content type="html">&lt;p&gt;It&amp;#39;s pretty easy to &lt;strong&gt;send&lt;/strong&gt; PowerShell &lt;strong&gt;results to Excel&lt;/strong&gt; and display them nicely as a spreadsheet. Actually, here&amp;#39;s a simple function called &lt;strong&gt;Out-ExcelReport&lt;/strong&gt;. Pipe anything to it, and it shows up as an excel sheet (provided you have indeed installed excel beforehand):&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;$env:temp\report$(Get-Date -format yyyyMMddHHmmss).csv&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;[&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;switch&lt;/span&gt;&lt;/span&gt;]&lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$open&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Input&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Export-Csv&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-NoTypeInformation&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-UseCulture&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Encoding&lt;/span&gt;&lt;/span&gt; UTF8&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$open&lt;/span&gt;&lt;/span&gt;) { &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Invoke-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; }&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;Now creating spreadsheets&amp;nbsp;is as easy as this:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Process&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-open&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h3&gt;Why Does Excel Display Garbage?&lt;/h3&gt;
&lt;p&gt;Displaying results in Excel isn&amp;#39;t so much a problem once you figure out how to export to CSV and then import that to excel. &lt;/p&gt;
&lt;p&gt;The problem becomes evident a bit later, when you &lt;strong&gt;start to have fun&lt;/strong&gt; and then suddenly &lt;strong&gt;run into lines&lt;/strong&gt; such as this one:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WMIObject&lt;/span&gt;&lt;/span&gt; Win32_NetworkAdapterConfiguration &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;filter&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;IPEnabled=True&amp;#39;&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; Caption, DefaultIPGateway, MACAddress, IPAddress | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-open&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;This line will indeed&amp;nbsp;find your active network adapters, and it will open excel and display the network properties your code selected. But... the result probably looks similar to this:&lt;/p&gt;
&lt;div class="pscode"&gt;Caption DefaultIPGateway MACAddress IPAddress&lt;br /&gt;[00000007] Bro... System.&lt;span class="datatype"&gt;&lt;span style="color:#0000ff;"&gt;String&lt;/span&gt;&lt;/span&gt;[] 68:A8:6D:0B:5F:CC System.&lt;span class="datatype"&gt;&lt;span style="color:#0000ff;"&gt;String&lt;/span&gt;&lt;/span&gt;[]&lt;/div&gt;
&lt;p&gt;Note how some properties display correctly (such as &lt;strong&gt;MACAddress&lt;/strong&gt;), while others read &amp;quot;&lt;strong&gt;System.String[]&lt;/strong&gt;&amp;quot;. Where did the IP address go?&lt;/p&gt;
&lt;h3&gt;Shape Up Your Results!&lt;/h3&gt;
&lt;p&gt;The problem occurs whenever the property is &lt;strong&gt;NOT just text&lt;/strong&gt;. Since excel can only deal with text, anything that is not text in the first place &lt;strong&gt;needs to be converted to text&lt;/strong&gt;. &lt;strong&gt;&amp;quot;System.String[]&amp;quot; &lt;/strong&gt;simply is the result of such a conversion. The property you tried to display was an array of strings. That&amp;#39;s correct BTW. A network adapter can have multiple IP addresses, so the property &lt;strong&gt;IPAddress&lt;/strong&gt; is by design an array.&lt;/p&gt;
&lt;p&gt;To &lt;strong&gt;create meaningful reports&lt;/strong&gt;, you need to convert such properties to text in a &lt;strong&gt;more clever way&lt;/strong&gt;. Here are two things you can do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Join Arrays:&lt;/strong&gt; use the &lt;strong&gt;-join operator&lt;/strong&gt; to join array elements and produce descriptive text&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Out-String:&lt;/strong&gt; send the property to &lt;strong&gt;Out-String&lt;/strong&gt; and take advantage of PowerShell&amp;#39;s own &lt;strong&gt;built-in conversion support&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So how exactly would you implement these conversion methods? Well, here&amp;#39;s a way:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$results&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WMIObject&lt;/span&gt;&lt;/span&gt; Win32_NetworkAdapterConfiguration &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;filter&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;IPEnabled=True&amp;#39;&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; Caption, DefaultIPGateway, MACAddress, IPAddress &lt;br /&gt;&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$results&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;DefaultIPGateway&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$results&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;DefaultIPGateway&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-join&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800000;"&gt;&lt;span class="string"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$results&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;IPAddress&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$results&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;IPAddress&lt;/span&gt;&lt;/span&gt; | &lt;span style="color:#5f9ea0;"&gt;&lt;span class="verbnoun"&gt;Out-String&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$results&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-open&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;Note how I just took the result produced by &lt;strong&gt;Select-Object&lt;/strong&gt; and saved that in $results. That&amp;#39;s all. Since &lt;strong&gt;Select-Object&lt;/strong&gt; always creates new objects with only the properties I want, and since objects produced by &lt;strong&gt;Select-Object&lt;/strong&gt; are always &lt;strong&gt;read/write&lt;/strong&gt;, I now can use whatever method I prefer to beautify those properties. Then, when all is fine, I can continue and send $results on to &lt;strong&gt;Out-ExcelReport&lt;/strong&gt;. The result is a report with all the multivalues displaying fine. &lt;/p&gt;
&lt;p&gt;Phew! Done - and you now can convert your PowerShell results to excel reports - and fine-tune any property that isn&amp;#39;t showing up the way you want it to. Stay tuned...!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=16128" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Array" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Array/default.aspx" /><category term="Excel" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Excel/default.aspx" /><category term="Out-String" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Out-String/default.aspx" /><category term="-join" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/-join/default.aspx" /></entry><entry><title>Finding Logged On User (and Writing To Other People's Registry)</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2012/04/19/finding-logged-on-user-and-writing-to-other-people-s-registry.aspx" /><id>/cs/blogs/tobias/archive/2012/04/19/finding-logged-on-user-and-writing-to-other-people-s-registry.aspx</id><published>2012-04-19T12:47:00Z</published><updated>2012-04-19T12:47:00Z</updated><content type="html">&lt;p&gt;How would I find out the &lt;strong&gt;currently logged on user on a remote machine&lt;/strong&gt;? And how can I remotely&amp;nbsp;access another person&amp;#39;s &lt;strong&gt;HKEY_CURRENT_USER&lt;/strong&gt; registry hive? Here are some tricks you may want to try...&lt;/p&gt;
&lt;h3&gt;Who&amp;#39;s Logged On?&lt;/h3&gt;
&lt;p&gt;To find out who&amp;#39;s logged on to another machine, you could use &lt;strong&gt;WMI&lt;/strong&gt; and check for the &lt;strong&gt;owner&lt;/strong&gt; of &lt;strong&gt;explorer.exe&lt;/strong&gt;. Since explorer.exe is providing the GUI, by looking at its owner you find the logged on users like this:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span style="color:#008000;"&gt;&lt;span class="comment"&gt;# specify the remote computer name or IP address you want to query&lt;/span&gt;&lt;br /&gt;&lt;span class="comment"&gt;# you do need remote WMI access to that machine&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$ComputerName&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$env:computername&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WMIObject&lt;/span&gt;&lt;/span&gt; Win32_Process &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;filter&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;name=&amp;quot;explorer.exe&amp;quot;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-computername&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$computername&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;ForEach-Object&lt;/span&gt;&lt;/span&gt; { &lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$owner&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;GetOwner&lt;/span&gt;&lt;/span&gt;()&lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;{0}\{1}&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-f&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$owner&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Domain&lt;/span&gt;&lt;/span&gt;, &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$owner&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;User&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;} | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Sort-Object&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Unique&lt;/span&gt;&lt;/span&gt; |&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;ForEach-Object&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$rv&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; 1 | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; ComputerName, User&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$rv&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;ComputerName&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$computername&lt;/span&gt;&lt;br /&gt;&lt;span class="var"&gt;$rv&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;User&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$_&lt;/span&gt;&lt;br /&gt;&lt;span class="var"&gt;$rv&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;/div&gt;
&lt;h3&gt;Writing to Other User&amp;#39;s HKEY_CURRENT_USER Registry Hive&lt;/h3&gt;
&lt;p&gt;Another common problem is remote registry access. There are many ways how you can remotely &lt;strong&gt;access the registry on another machine&lt;/strong&gt;. Today, I will be using PowerShell remoting so as a prerequisite, PowerShell remoting needs to be set up and enabled (I am not covering that part here).&lt;/p&gt;
&lt;p&gt;All of the following examples run locally and remotely. To run them &lt;strong&gt;remotely&lt;/strong&gt;, use &lt;strong&gt;Invoke-Command&lt;/strong&gt; and send the code to the remote system via &lt;strong&gt;PowerShell Remoting&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When you connect to a remote machine via PowerShell remoting, you can easily write to &lt;strong&gt;HKEY_LOCAL_MACHINE&lt;/strong&gt;, but writing to &lt;strong&gt;HKEY_CURRENT_USER&lt;/strong&gt; would always write to your very own &lt;strong&gt;HKEY_CURRENT_USER&lt;/strong&gt; and not to the one of the person currently logged on to that machine. This is because PowerShell remoting uses &lt;strong&gt;your identity&lt;/strong&gt; (and not the one of the currently logged on remote user).&lt;/p&gt;
&lt;p&gt;However, as long as there *is* someone logged on the remote machine, you can easily write to that person&amp;#39;s &lt;strong&gt;HKEY_CURRENT_USER&lt;/strong&gt; as well. First, you need to know &lt;strong&gt;who is logged on&lt;/strong&gt;. Here&amp;#39;s how you can find that out:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-CurrentUserName&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-ItemProperty&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;Registry::HKEY_USERS\*\Volatile*&amp;#39;&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-ExpandProperty&lt;/span&gt;&lt;/span&gt; UserName&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;Basically, we are looking into &lt;strong&gt;HKEY_USERS&lt;/strong&gt; and search for any user that has a subkey that starts with &amp;quot;&lt;strong&gt;Volatile&lt;/strong&gt;&amp;quot;, then we can read the registry value &amp;quot;UserName&amp;quot;. That&amp;#39;s it.&lt;/p&gt;
&lt;h3&gt;Accessing Logged On User via HKEY_USERS&lt;/h3&gt;
&lt;p&gt;By taking the same route, we can now write to another users&amp;#39; private registry hive. This is how you find the hive that represents this user:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-CurrentUser&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Split-Path&lt;/span&gt;&lt;/span&gt; (dir &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;Registry::HKEY_USERS\*\Volatile*&amp;#39;&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-ExpandProperty&lt;/span&gt;&lt;/span&gt; Name)&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;To create a new key named &amp;quot;NewKey&amp;quot; in &lt;strong&gt;HKEY_CURRENT_USER\Software&lt;/strong&gt;, try this:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$user&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#5f9ea0;"&gt;&lt;span class="verbnoun"&gt;Get-CurrentUser&lt;/span&gt;&lt;br /&gt;&lt;span class="verbnoun"&gt;New-Item&lt;/span&gt;&lt;/span&gt; Registry::&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$user&lt;/span&gt;&lt;/span&gt;\Software\NewKey&lt;/div&gt;
&lt;p&gt;Now, the key was written to &lt;strong&gt;HKCU:&lt;/strong&gt; in the context of the currently logged on user on the remote machine.&lt;/p&gt;
&lt;p&gt;I hope that was a useful hint. Stay tuned...!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=16043" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Registry" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Registry/default.aspx" /><category term="WMI" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/WMI/default.aspx" /><category term="HKCU" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/HKCU/default.aspx" /><category term="Remote" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Remote/default.aspx" /></entry><entry><title>Shorten Path Names (and using Low Level API functions)</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2012/04/11/shorten-path-names-and-using-low-level-api-functions.aspx" /><id>/cs/blogs/tobias/archive/2012/04/11/shorten-path-names-and-using-low-level-api-functions.aspx</id><published>2012-04-11T07:22:00Z</published><updated>2012-04-11T07:22:00Z</updated><content type="html">&lt;p&gt;Path names can grow large, and sometimes there is just not enough room to display the entire path.&amp;nbsp; There is no cmdlet either that would shorten the path for you. So let&amp;#39;s take a look today at how you can &lt;strong&gt;access low-level Windows API functions from PowerShell&lt;/strong&gt; to add this feature,&lt;/p&gt;
&lt;p&gt;As an immediate benefit, after you read this article you can optimize your PowerShell prompt and make it shorter while keeping the current path meaningful.&lt;/p&gt;
&lt;h3&gt;Mission Target: Compacting a Path&lt;/h3&gt;
&lt;p&gt;What exactly is meant by &amp;quot;shorten a path name&amp;quot;? I am not talking about turning a &amp;quot;long&amp;quot; file path into the old 8.3 file path. That&amp;#39;s a different ball game - the 8.3 path would be shorter but at the same time completely unreadable.&lt;/p&gt;
&lt;p&gt;What I want is to shorten a path in a &lt;strong&gt;human readable format&lt;/strong&gt;, keeping as much meaningful information as possible. And while there is no PowerShell cmdlet for this, there is a low level Windows API function called &lt;strong&gt;PathCompactPathEx()&lt;/strong&gt; which lives in the system file&lt;strong&gt; shlwapi.dll&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;In pre-PowerShell-times, low level API functions were only available to developers. With PowerShell, everyone can access them. And here&amp;#39;s how.&lt;/p&gt;
&lt;h3&gt;Accessing Low Level API Functions&lt;/h3&gt;
&lt;p&gt;To access a low level API function, you need its &amp;quot;&lt;strong&gt;signature&lt;/strong&gt;&amp;quot; which describes where the function lives and how it needs to be called (its parameters and data types). Typically, the signature is defined in a language like&lt;strong&gt; c#&lt;/strong&gt;, but you could as well use signatures defined in any other language that is supported by the .NET framework, like &lt;strong&gt;VB.NET&lt;/strong&gt;. There are plenty of web sites that list API signatures, like &lt;strong&gt;pinvoke.net&lt;/strong&gt;. Here&amp;#39;s the signature for &lt;strong&gt;PathCompactPathEx() &lt;/strong&gt;in c#:&lt;/p&gt;
&lt;div class="pscode"&gt;[DllImport(&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;shlwapi.dll&amp;quot;&lt;/span&gt;&lt;/span&gt;, CharSet &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;CharSet.Auto&lt;/span&gt;&lt;/span&gt;, SetLastError &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; true)] public static extern &lt;span class="datatype"&gt;&lt;span style="color:#0000ff;"&gt;bool&lt;/span&gt;&lt;/span&gt; PathCompactPathEx(&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.Text.StringBuilder&lt;/span&gt;&lt;/span&gt; pszOut, &lt;span class="datatype"&gt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;&lt;/span&gt; pszSrc, Int32 cchMax, Int32 dwFlags);&lt;/div&gt;
&lt;p&gt;Let&amp;#39;s try and &lt;strong&gt;decipher it&lt;/strong&gt; (you can skip this part if your hair is on fire and you just want results): &lt;/p&gt;
&lt;p&gt;It tells you that there is a function called &lt;strong&gt;PathCompactPathEx()&lt;/strong&gt; which resides in the system file &lt;strong&gt;shlwapi.dll&lt;/strong&gt; and takes &lt;strong&gt;four input parameters&lt;/strong&gt; and returns a Boolean value. The four parameters needed to run the function have &lt;strong&gt;different data types&lt;/strong&gt;. The first one is a &lt;strong&gt;StringBuilder&lt;/strong&gt; type, the second one is a &lt;strong&gt;String&lt;/strong&gt; (text), and the remainder are &lt;strong&gt;Int32&lt;/strong&gt; (32bit whole numbers). The signature also states that this is a &amp;quot;&lt;strong&gt;static&lt;/strong&gt;&amp;quot; function. Static functions will later become a part of a data type, so the keyword &amp;quot;static&amp;quot; is an important piece of information once you are ready to call your API function from PowerShell.&lt;br /&gt;And this is how you feed a signature into PowerShell to gain access to that function:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$sig&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; @&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;[DllImport(&amp;quot;shlwapi.dll&amp;quot;, CharSet = CharSet.Auto, SetLastError = true)]&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;public static extern bool PathCompactPathEx(&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span style="color:#800000;"&gt;&lt;span class="string"&gt;System.Text.StringBuilder pszOut, string pszSrc, Int32 cchMax, &lt;/span&gt;&lt;br /&gt;&lt;span class="string"&gt;Int32 dwFlags); &lt;/span&gt;&lt;br /&gt;&lt;span class="string"&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;@&lt;br /&gt;&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Add-Type&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-MemberDefinition&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$sig&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Namespace&lt;/span&gt;&lt;/span&gt; Win32 &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Name&lt;/span&gt;&lt;/span&gt; StringFunctions&lt;/div&gt;
&lt;p&gt;The secret really is to feed that signature code to the cmdlet &lt;strong&gt;Add-Type&lt;/strong&gt;. This cmdlet then creates a new type (which you can assign any name you want; in this example the type is called Win32.StringFunctions), and you can use the new type then to access the function.&lt;/p&gt;
&lt;p&gt;Two things are worth mentioning here: &lt;/p&gt;
&lt;p&gt;First, I am submitting the signature as a &amp;quot;&lt;strong&gt;Here-String&lt;/strong&gt;&amp;quot;, which is a multiline text delimited by @&amp;#39;&amp;hellip;&amp;#39;@. For Here-Strings to work, it is of &lt;strong&gt;utmost importance&lt;/strong&gt; that no text whatsoever (including whitespace) follows the opening tag (@&amp;#39;), and that no text (including whitespace) preceeds the closing tag (&amp;#39;@). &lt;/p&gt;
&lt;p&gt;Second, &lt;strong&gt;Add-Type&lt;/strong&gt; is actually taking the c# signature source code and is compiling a DLL in memory which then is loaded. If your signature is written in a different language (like VB.NET), you need to specify the language by adding the parameter &lt;strong&gt;-Language&lt;/strong&gt;. Valid languages currently are &lt;strong&gt;CSharp&lt;/strong&gt;, &lt;strong&gt;CSharpVersion3&lt;/strong&gt;, &lt;strong&gt;CSharpVersion2&lt;/strong&gt;, &lt;strong&gt;VisualBasic&lt;/strong&gt;, and &lt;strong&gt;Jscript&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;After running &lt;strong&gt;Add-Type&lt;/strong&gt;, you actually get a new type as the name suggests. The name of this type was set by the parameters &lt;strong&gt;-Namespace&lt;/strong&gt; and &lt;strong&gt;-Name&lt;/strong&gt;, so in our example the name of the new type is &lt;strong&gt;Win32.StringFunctions&lt;/strong&gt;. To check out which functions are available in this type, use this line:&lt;/p&gt;
&lt;p&gt;PS&amp;gt; [&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;Win32.StringFunctions&lt;/span&gt;&lt;/span&gt;] | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Member&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#5f9ea0;"&gt;&lt;span class="modifier"&gt;-Static&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;TypeName: &lt;span style="color:#8b4513;"&gt;&lt;span class="namespace"&gt;Win32.StringFunctions&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Name MemberType Definition&lt;br /&gt;&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;----&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;----------&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;&lt;span class="op"&gt;----------&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Equals Method static &lt;span class="datatype"&gt;&lt;span style="color:#0000ff;"&gt;bool&lt;/span&gt;&lt;/span&gt; Equals(&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.Object&lt;/span&gt;&lt;/span&gt; objA, &lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.Object&lt;/span&gt;&lt;/span&gt; objB)&lt;br /&gt;PathCompactPathEx Method static &lt;span class="datatype"&gt;&lt;span style="color:#0000ff;"&gt;bool&lt;/span&gt;&lt;/span&gt; PathCompactPathEx(&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.Text.StringBuilder&lt;/span&gt;&lt;/span&gt; p...&lt;br /&gt;ReferenceEquals Method static &lt;span class="datatype"&gt;&lt;span style="color:#0000ff;"&gt;bool&lt;/span&gt;&lt;/span&gt; ReferenceEquals(&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.Object&lt;/span&gt;&lt;/span&gt; objA, &lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.Ob&lt;/span&gt;&lt;/span&gt;...&lt;/p&gt;
&lt;p&gt;And there is our new API function &lt;strong&gt;PathCompactPathEx()&lt;/strong&gt;! Don&amp;#39;t worry about the other two, &lt;strong&gt;Equals()&lt;/strong&gt; and &lt;strong&gt;ReferenceEquals()&lt;/strong&gt;, these are default functions provided by every type.&lt;/p&gt;
&lt;h3&gt;Turning Low-Level Functions Into Civilized Functions&lt;/h3&gt;
&lt;p&gt;Accessing our low level function is still not a piece of cake, however, because as the term &amp;quot;low level&amp;quot; suggests, these API functions work in a very basic fashion and require you to pay attention to the required data types and containers. That&amp;#39;s why it is a &lt;strong&gt;best practice to turn a hard-to-use low level function into an easy-to-use PowerShell function&lt;/strong&gt; before you actually start using it in your scripts.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s the result (which you can also download here: &lt;a target="_blank" href="http://powershell.com/cs/media/p/15905.aspx"&gt;http://powershell.com/cs/media/p/15905.aspx&lt;/a&gt;):&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Compress-Path&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Path&lt;/span&gt;&lt;/span&gt;, &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Length&lt;/span&gt;&lt;/span&gt;&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt;20) {&lt;br /&gt;&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$sig&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; @&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;[DllImport(&amp;quot;shlwapi.dll&amp;quot;, CharSet = CharSet.Auto, SetLastError = true)]&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;public static extern bool PathCompactPathEx(&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span style="color:#800000;"&gt;&lt;span class="string"&gt;System.Text.StringBuilder pszOut, string pszSrc, Int32 cchMax, &lt;/span&gt;&lt;br /&gt;&lt;span class="string"&gt;Int32 dwFlags); &lt;/span&gt;&lt;br /&gt;&lt;span class="string"&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;@&lt;br /&gt;&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Add-Type&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-MemberDefinition&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$sig&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-name&lt;/span&gt;&lt;/span&gt; StringFunctions &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-namespace&lt;/span&gt;&lt;/span&gt; Win32&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$sb&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;New-Object&lt;/span&gt;&lt;/span&gt; &lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.Text.StringBuilder&lt;/span&gt;&lt;/span&gt;(260)&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt; ([&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;Win32.StringFunctions&lt;/span&gt;&lt;/span&gt;]::&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;PathCompactPathEx&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$sb&lt;/span&gt;&lt;/span&gt; , &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Path&lt;/span&gt;&lt;/span&gt; , &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Length&lt;/span&gt;&lt;/span&gt;&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;+&lt;/span&gt;&lt;/span&gt;1, 0)) {&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$sb&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;ToString&lt;/span&gt;&lt;/span&gt;()&lt;br /&gt;} &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;else&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;Throw &lt;span style="color:#800000;"&gt;&lt;span class="string"&gt;&amp;quot;Unable to compact path&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;Now, to compact a path all you need to do is call Compress-Path and tell the function how much room you have:&lt;/p&gt;
&lt;p&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Compress-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$env:temp&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Length&lt;/span&gt;&lt;/span&gt; 30&lt;br /&gt;C:\Users\Tobias\AppDat...\Temp&lt;br /&gt;PS&amp;gt;&lt;br /&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Compress-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Len&lt;/span&gt;&lt;/span&gt; 16&lt;br /&gt;HKL...\Uninstall&lt;br /&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Compress-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Len&lt;/span&gt;&lt;/span&gt; 30&lt;br /&gt;HKLM:\Software\Mi...\Uninstall&lt;/p&gt;
&lt;h3&gt;A better PowerShell prompt&lt;/h3&gt;
&lt;p&gt;One first use of &lt;strong&gt;Compress-Path&lt;/strong&gt; could be an improved &lt;strong&gt;prompt&lt;/strong&gt; function. By default, the PowerShell prompt always displays the complete path you are in, often wasting a lot of real estate in your console or editor.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s an improved prompt function that displays a compressed path, still giving you a good indication of where your current folder is, and displays the complete path in your window title bar.&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; prompt {&lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;PS {0}&amp;gt; &amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-f&lt;/span&gt;&lt;/span&gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Compress-Path&lt;/span&gt;&lt;/span&gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Location&lt;/span&gt;&lt;/span&gt;) 15)&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$host&lt;/span&gt;&lt;/span&gt;.&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;UI.RawUI.WindowTitle&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Location&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;To permanently use this prompt, just make sure you add both the function &lt;strong&gt;Compress-Path&lt;/strong&gt; and the function &lt;strong&gt;prompt&lt;/strong&gt; into your profile script so PowerShell loads both functions automatically at startup (both functions can be downloaded here: &lt;a target="_blank" href="http://powershell.com/cs/media/p/15905.aspx"&gt;http://powershell.com/cs/media/p/15905.aspx&lt;/a&gt;). The profile script is located here:&lt;/p&gt;
&lt;div class="pscode"&gt;PS C:\Us...\Tobias&amp;gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$profile&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;CurrentUserAllHosts&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;C:\Users\Tobias\Documents\WindowsPowerShell\&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;profile.ps1&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;And here&amp;#39;s some code that opens the profile script for you (and creates it if it does not yet exist):&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;!&lt;/span&gt;&lt;/span&gt;(&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Test-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$profile&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;CurrentUserAllHosts&lt;/span&gt;&lt;/span&gt;)) &lt;br /&gt;{ &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$null&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;New-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$profile&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;CurrentUserAllHosts&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Force&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-ItemType&lt;/span&gt;&lt;/span&gt; File }; &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Invoke-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$profile&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;CurrentUserAllHosts&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;I hope you had fun! You can now take this concept and wrap all kinds of low-level API functions in the same way. Stay tuned...!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=15906" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="API" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/API/default.aspx" /><category term="Here-String" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Here-String/default.aspx" /><category term="PathCompactPathEx" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/PathCompactPathEx/default.aspx" /><category term="prompt" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/prompt/default.aspx" /><category term="Add-Type" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Add-Type/default.aspx" /></entry><entry><title>Regular Expressions Are Your Friend (Part 3)</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/12/02/regular-expressions-are-your-friend-part-3.aspx" /><id>/cs/blogs/tobias/archive/2011/12/02/regular-expressions-are-your-friend-part-3.aspx</id><published>2011-12-02T12:27:00Z</published><updated>2011-12-02T12:27:00Z</updated><content type="html">&lt;p&gt;In &lt;a target="_blank" href="http://powershell.com/cs/blogs/tobias/archive/2011/10/27/regular-expressions-are-your-friend-part-1.aspx"&gt;Part 1&lt;/a&gt;, you lerned how Regular Expressions can extract useful information from noise text. In &lt;a target="_blank" href="http://powershell.com/cs/blogs/tobias/archive/2011/11/14/regular-expressions-are-your-friend-part-2.aspx"&gt;Part 2&lt;/a&gt;, we looked at using Regular Expressions to split text, yet another powerful technique to extract the pieces of information you may need. Today, we conclude our little excurse and use Regular Expressions to &lt;strong&gt;replace text&lt;/strong&gt;. Fasten your seat belts, please!&lt;/p&gt;
&lt;h3&gt;Replacing Old Text With New Stuff&lt;/h3&gt;
&lt;p&gt;Replacing some information inside a text with some other information seems to be pretty straight-forward: simply use &lt;strong&gt;-replace&lt;/strong&gt; and tell the operator which text you would like to replace with other text:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;#39;Hello World!&amp;#39; -replace &amp;#39;World&amp;#39;, &amp;#39;PowerShell&amp;#39;&lt;br /&gt;Hello PowerShell!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It is not obvious at first that &lt;strong&gt;-replace&lt;/strong&gt; in reality takes a Regular Expression. You may notice accidentally when you try and use characters that have a special meaning, like here:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;#39;Hello.&amp;#39; -replace &amp;#39;.&amp;#39;, &amp;#39;!&amp;#39;&lt;br /&gt;!!!!!!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Huh? Well, &amp;quot;.&amp;quot; is a special RegEx placeholder and represents any character, so the result is right. If you must use &lt;strong&gt;special characters&lt;/strong&gt;, they &lt;strong&gt;need to be escaped&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;#39;Hello.&amp;#39; -replace &amp;#39;\.&amp;#39;, &amp;#39;!&amp;#39;&lt;br /&gt;Hello!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Remember the trick from our previous parts? If you aren&amp;#39;t sure which characters need to be escaped, ask PowerShell!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; [RegEx]::Escape(&amp;#39;Hello.&amp;#39;)&lt;br /&gt;Hello\.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Eliminating Duplicates&lt;/h3&gt;
&lt;p&gt;Being able to use Regular Expressions does add the burden of escaping, but at the same time it adds incredible power. Let&amp;#39;s assume you want to &lt;strong&gt;get rid of excessive whitespace&lt;/strong&gt; and replace multiple whitespace with just one space. Here is the solution:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;Too&amp;nbsp;&amp;nbsp; many&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; blank&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; characters&amp;#39;&lt;br /&gt;PS&amp;gt; $text -replace &amp;#39;\s{2,}&amp;#39;, &amp;#39; &amp;#39;&lt;br /&gt;Too many blank characters&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here, &lt;strong&gt;-replace&lt;/strong&gt; was looking for any whitespace (placeholder: &lt;strong&gt;\s&lt;/strong&gt;) that occurs at least 2 times (quantifier: &lt;strong&gt;{2,}&lt;/strong&gt;). It then replaces these instances with a single space. Done.&lt;/p&gt;
&lt;h3&gt;Using The Match&lt;/h3&gt;
&lt;p&gt;Your new replacement text does not need to be static. It can &lt;strong&gt;reference the original match&lt;/strong&gt;. Have a look:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;The problem was described in KB123456. Look it up.&amp;#39;&lt;br /&gt;PS&amp;gt; $pattern = &amp;#39;KB\d{6,8}&amp;#39;&lt;br /&gt;PS&amp;gt; $text -replace $pattern, &amp;#39;a KB-Article ($0 to be precise)&amp;#39;&lt;br /&gt;The problem was described in a KB-Article (KB123456 to be precise). Look it up.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here, we define a pattern for KB article numbers. The replacement string contains a placeholder (&lt;strong&gt;$0&lt;/strong&gt;). This placeholder &lt;strong&gt;seems to be a PowerShell variable&lt;/strong&gt;, but it is not. It &lt;strong&gt;just looks like one&lt;/strong&gt;, which is why you &lt;strong&gt;must&lt;/strong&gt; use single quotes or else PowerShell would also interpret this as a variable and resolve it.&lt;/p&gt;
&lt;p&gt;What exactly is &lt;strong&gt;$0&lt;/strong&gt;? To explain this, take a look at the result returned by &lt;strong&gt;-match&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;The problem was described in KB123456. Look it up.&amp;#39;&lt;br /&gt;PS&amp;gt; $pattern = &amp;#39;KB\d{6,8}&amp;#39;&lt;br /&gt;PS&amp;gt; $text -match $pattern&lt;br /&gt;True&lt;br /&gt;PS&amp;gt; $matches&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Value&lt;br /&gt;----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -----&lt;br /&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KB123456&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As it turns out,&lt;strong&gt; $matches&lt;/strong&gt; returns a match with the name &amp;quot;0&amp;quot;, so this match is what &lt;strong&gt;$0&lt;/strong&gt; represents. &lt;/p&gt;
&lt;p&gt;If you use &lt;strong&gt;grouping&lt;/strong&gt; in your pattern (place parts of it in brackets), then you have even more choices:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;The problem was described in KB123456. Look it up.&amp;#39;&lt;br /&gt;PS&amp;gt; $pattern = &amp;#39;KB(\d{6,8})&amp;#39;&lt;br /&gt;PS&amp;gt; $text -match $pattern&lt;br /&gt;True&lt;br /&gt;PS&amp;gt; $matches&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Value&lt;br /&gt;----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -----&lt;br /&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 123456&lt;br /&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KB123456&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now, &lt;strong&gt;-replace&lt;/strong&gt; would support both the placeholders &lt;strong&gt;$0&lt;/strong&gt; and &lt;strong&gt;$1&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;The problem was described in KB123456. Look it up.&amp;#39;&lt;br /&gt;PS&amp;gt; $pattern = &amp;#39;KB(\d{6,8})&amp;#39;&lt;br /&gt;PS&amp;gt; $text -replace $pattern, &amp;#39;a KB-Article (with number $1 to be precise)&amp;#39;&lt;br /&gt;The problem was described in a KB-Article (with number 123456 to be precise). Look it up.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Using Backreferences&lt;/h3&gt;
&lt;p&gt;What you have just seen is often called &amp;quot;backreference&amp;quot;: your new replacement text is &lt;strong&gt;backreferencing the original text&lt;/strong&gt;. That technique can be extremely versatile. Let&amp;#39;s say you want to make sure &lt;strong&gt;each comma in a text is followed by a space&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;Here is a solution that uses backreferences:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;After each comma,I want a whitespace,but only if there was no whitespace in the first place, of course.&amp;#39;&lt;br /&gt;PS&amp;gt; $text -replace&amp;nbsp; &amp;#39;,(\S)&amp;#39;, &amp;#39;, $1&amp;#39;&lt;br /&gt;After each comma, I want a whitespace, but only if there was no whitespace in the first place, of course.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The pattern looks for a comma that is immediately followed by a non-whitespace (placeholder: &lt;strong&gt;/S&lt;/strong&gt; - watch out, placeholders are &lt;strong&gt;case-sensitive&lt;/strong&gt;!). If that was found, it is replaced by a comma, a space and then the backreference (which is the character that previously immediately followed the comma). Mission accomplished. Thank you, backreferences!&lt;/p&gt;
&lt;h3&gt;Finding Duplicates - Backreferences In Your Pattern&lt;/h3&gt;
&lt;p&gt;You can even find and eliminate unwanted duplicates. Here is some sample code for you:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;this text text contains duplicate words words following each other&amp;#39;&lt;br /&gt;PS&amp;gt; $text -replace &amp;#39;\b(\w+)(\s+\1){1,}\b&amp;#39;, &amp;#39;$1&amp;#39;&lt;br /&gt;this text contains duplicate words following each other&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this case, &lt;strong&gt;the original pattern&lt;/strong&gt; itself uses backreferences, too. It looks for a word boundary (placeholder: &lt;strong&gt;\b&lt;/strong&gt;) and then for a word (&lt;strong&gt;\w+&lt;/strong&gt;). Note that the word pattern is in brackets, so it is the first group with the index &lt;strong&gt;1&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;Then, it looks for at least one space and the same word again (placeholder: &lt;strong&gt;\1&lt;/strong&gt;). So it is backreferencing the first group match. The quantifier (&lt;strong&gt;{1,}&lt;/strong&gt;) tells RegEx that we are looking for one or more duplicate words. &lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;-replace&lt;/strong&gt; operator then replaces all of this with the backreference to the first word, thus eliminating all the duplicate words.&lt;/p&gt;
&lt;p&gt;So if you want to backreference matches in your replacement text, you prefix the group number with a &lt;strong&gt;$&lt;/strong&gt;. If you want to backreference matches in your pattern, prefix the index with &lt;strong&gt;\&lt;/strong&gt; instead.&lt;/p&gt;
&lt;h3&gt;Capitalizing First Letter in a Word&lt;/h3&gt;
&lt;p&gt;Every now and then, even Regular Expressions can&amp;#39;t do the job, so PowerShell scriptblocks can assist. Let&amp;#39;s assume you want to &lt;strong&gt;capitalize every first letter in every word&lt;/strong&gt;. Identifying the first letter is easy, but &lt;strong&gt;Regular Expressions cannot capitalize characters&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;That&amp;#39;s why you can &lt;strong&gt;submit a PowerShell script block&lt;/strong&gt; that tells RegEx what to do with the match. Here is a sample:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;i want to capitalize every word in this sentence.&amp;#39;&lt;br /&gt;PS&amp;gt; $pattern = &amp;#39;\b(\w)&amp;#39;&lt;br /&gt;PS&amp;gt; [RegEx]::Replace($text, $pattern, { param($letter) $letter.Value.toUpper() })&lt;br /&gt;I Want To Capitalize Every Word In This Sentence.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Since &lt;strong&gt;-replace&lt;/strong&gt; cannot process PowerShell script blocks, you need to use .NET directly here. The pattern describes a word boundary (placeholder: &lt;strong&gt;\b&lt;/strong&gt;) and then the immediately following first character (placeholder: &lt;strong&gt;\w&lt;/strong&gt;). Note that this placeholder is placed in brackets, so it is the first group.&lt;/p&gt;
&lt;p&gt;Next, this sample uses the .NET class &lt;strong&gt;[RegEx]&lt;/strong&gt; and its &lt;strong&gt;Replace()&lt;/strong&gt; method which is the same dog food &lt;strong&gt;-replace&lt;/strong&gt; uses internally, too. This time, though, a script block is submitted as replacement text. &lt;/p&gt;
&lt;p&gt;This script block is often called a &amp;quot;&lt;strong&gt;delegate&lt;/strong&gt;&amp;quot; because it is called for each match and determines the replacement dynamically. It receives the match through a &lt;strong&gt;param()&lt;/strong&gt; block and then can use whatever logic you want to return the replacement text. In our case, it simply converts the received letter to uppercase.&lt;/p&gt;
&lt;h3&gt;What&amp;rsquo;s Next?&lt;/h3&gt;
&lt;p&gt;We are done with our tour of PowerShells&amp;#39; RegEx support. Next time, we check out &lt;strong&gt;hashtables&lt;/strong&gt; and the clever things you can do with them. Hashtables are really everywhere in PowerShell, but they are a little shy, so you won&amp;#39;t see them at first. Knowing about hashtables, how they work and what can be done with them&amp;nbsp;will get you a lot more control over PowerShell. I am sure you&amp;#39;ll be surprised! Stay tuned...&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=13446" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Replace" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Replace/default.aspx" /><category term="-replace" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/-replace/default.aspx" /><category term="Regular Expression" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Regular+Expression/default.aspx" /><category term="RegEx" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/RegEx/default.aspx" /><category term="delegate" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/delegate/default.aspx" /></entry><entry><title>Regular Expressions Are Your Friend (Part 2)</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/11/14/regular-expressions-are-your-friend-part-2.aspx" /><id>/cs/blogs/tobias/archive/2011/11/14/regular-expressions-are-your-friend-part-2.aspx</id><published>2011-11-14T10:37:00Z</published><updated>2011-11-14T10:37:00Z</updated><content type="html">&lt;p&gt;In &lt;a target="_blank" href="http://powershell.com/cs/blogs/tobias/archive/2011/10/27/regular-expressions-are-your-friend-part-1.aspx"&gt;Part 1&lt;/a&gt;, we used &lt;strong&gt;Regular Expressions&lt;/strong&gt; to extract useful information from noise text. That was pretty cool, and we used the operator &lt;strong&gt;-match&lt;/strong&gt;, the automatic PowerShell variable &lt;strong&gt;$matches&lt;/strong&gt; and the powerful &lt;strong&gt;Get-Matches&lt;/strong&gt; function.&lt;/p&gt;
&lt;p&gt;Today, we&amp;#39;ll use Regular Expressions again, but this time they are used&amp;nbsp;to split text. &lt;/p&gt;
&lt;h3&gt;Chopping Text Into Pieces&lt;/h3&gt;
&lt;p&gt;Regular Expressions can be your &lt;strong&gt;butchers&amp;#39; knife to split text&lt;/strong&gt; into pieces. Why on earth would you like do that, though? &lt;/p&gt;
&lt;p&gt;For example, to process a comma-separated list of values. Have a look:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;#39;comma,separated,list&amp;#39; -split &amp;#39;,&amp;#39;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here, the operator &lt;strong&gt;-split&lt;/strong&gt; takes the text on its left side and chops the text up at each comma it finds. Yes, Regular Expressions do not need to be monsters. They can be very small. In this case, our Regular Expression simply is the comma - and represents a comma. It is - uh - a placeholder for - a comma. Easy enough. &lt;/p&gt;
&lt;p&gt;You now could pipe the results to &lt;strong&gt;Foreach-Object&lt;/strong&gt; and do stuff for all the elements in your comma-separated list:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;$computers = &amp;#39;pc1,pc2,server12,serverAB&amp;#39;&lt;br /&gt;$computers -split &amp;#39;,&amp;#39; | Foreach-Object { &amp;quot;I am doing something with $_&amp;quot; }&lt;br /&gt;I am doing something with pc1&lt;br /&gt;I am doing something with pc2&lt;br /&gt;I am doing something with server12&lt;br /&gt;I am doing something with serverAB&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;$computers -split &amp;#39;,&amp;#39; | Foreach-Object {&amp;nbsp;Get-WMIObject Win32_BIOS -computername $_&amp;nbsp;}&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can also use splitting to take apart &lt;strong&gt;path names&lt;/strong&gt;. Here, the pattern is the backslash (if you want the parts of the path) or a dot (if you are after the &lt;strong&gt;file extension&lt;/strong&gt;). This time, however, the pattern to use is not as straight-forward. Both the backslash and the dot are &lt;strong&gt;special characters in Regular Expressions&lt;/strong&gt;. So if you really mean a backslash or a dot, you need to &lt;strong&gt;escape them&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;#39;c:\windows\file.txt&amp;#39; -split &amp;#39;\.&amp;#39;&lt;br /&gt;c:\windows\file&lt;br /&gt;txt&lt;br /&gt;PS&amp;gt; (&amp;#39;c:\windows\file.txt&amp;#39; -split &amp;#39;\.&amp;#39;)[-1]&lt;br /&gt;txt&lt;br /&gt;PS&amp;gt; &amp;#39;c:\windows\file.txt&amp;#39; -split &amp;#39;\\&amp;#39;&lt;br /&gt;c:&lt;br /&gt;windows&lt;br /&gt;file.txt&lt;br /&gt;PS&amp;gt; (&amp;#39;c:\windows\file.txt&amp;#39; -split &amp;#39;\\&amp;#39;)[-1]&lt;br /&gt;file.txt&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Escaping Regular Expressions&lt;/h3&gt;
&lt;p&gt;As you have just discovered, sometimes it is necessary to escape characters in Regular Expressions so they are taken literally. &lt;/p&gt;
&lt;p&gt;Fortunately, there is a way to &lt;strong&gt;automatically escape characters&lt;/strong&gt;. If you aren&amp;#39;t sure if something you mean literally should indeed be escaped or not, try using this trick:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; [Regex]::Escape(&amp;#39;c:\folder\file.txt&amp;#39;)&lt;br /&gt;c:\\folder\\file\.txt&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It takes the text you provide and returns it in escaped form so Regular Expressions wouldn&amp;#39;t treat anything in that text as special characters.&lt;/p&gt;
&lt;h3&gt;Splitting Without Consuming&lt;/h3&gt;
&lt;p&gt;By default, when you chop up text using &lt;strong&gt;-split&lt;/strong&gt;, the split character(s) are &amp;quot;consumed&amp;quot;, so they are gone after the split. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;This text gets splitted, and the delimiter is by default consumed, as you can see&amp;#39;&lt;br /&gt;PS&amp;gt; $text -split &amp;#39;,&amp;#39;&lt;br /&gt;This text gets splitted&lt;br /&gt;&amp;nbsp;and the delimiter is by default consumed&lt;br /&gt;&amp;nbsp;as you can see&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;With look-behind expressions, you can make sure that the splitting pattern is not consumed:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text -split &amp;#39;(?&amp;lt;=, )&amp;#39;&lt;br /&gt;This text gets splitted,&lt;br /&gt;and the delimiter is by default consumed,&lt;br /&gt;as you can see&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Basically, &amp;quot;?&amp;lt;=&amp;quot; tells the RegEx engine to split &lt;em&gt;right after&lt;/em&gt; the splitting character. Splitting without consuming can become important as you&amp;#39;ll see in a second.&lt;/p&gt;
&lt;h3&gt;Separating Hex-Pairs&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s assume you have a long &lt;strong&gt;list of hexadecimal pairs &lt;/strong&gt;and would like to &lt;strong&gt;convert&lt;/strong&gt; that list back to &lt;strong&gt;decimals&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;#39;00AB1CFFAB1034&amp;#39; &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To do that, you would somehow have to split after every second character and without consuming because you do not want to loose any information. Here is how you solve this puzzle:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;#39;00AB1CFFAB1034&amp;#39; -split &amp;#39;(?&amp;lt;=\G[0-9a-f]{2})(?=.)&amp;#39;&lt;br /&gt;00&lt;br /&gt;AB&lt;br /&gt;1C&lt;br /&gt;FF&lt;br /&gt;AB&lt;br /&gt;10&lt;br /&gt;34&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now you can pipe the pairs to &lt;strong&gt;Foreach-Object&lt;/strong&gt; and convert them to decimals or bytes or whatever you may need:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;#39;00AB1CFFAB1034&amp;#39; -split &amp;#39;(?&amp;lt;=\G[0-9a-f]{2})(?=.)&amp;#39; | &lt;br /&gt;&amp;nbsp; Foreach-Object { [System.Convert]::ToInt32($_, 16) }&lt;br /&gt;0&lt;br /&gt;171&lt;br /&gt;28&lt;br /&gt;255&lt;br /&gt;171&lt;br /&gt;16&lt;br /&gt;52&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;#39;00AB1CFFAB1034&amp;#39; -split &amp;#39;(?&amp;lt;=\G[0-9a-f]{2})(?=.)&amp;#39; | &lt;br /&gt;&amp;nbsp; Foreach-Object { [System.Convert]::ToByte($_, 16) }&lt;br /&gt;0&lt;br /&gt;171&lt;br /&gt;28&lt;br /&gt;255&lt;br /&gt;171&lt;br /&gt;16&lt;br /&gt;52&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Whew, wait a minute! This time, the Regular Expression wasn&amp;#39;t as simple anymore. Right. &lt;strong&gt;Regular Expressions aren&amp;#39;t hated without a reason&lt;/strong&gt;. Sometimes they become tricky little monsters with mind-blowing &lt;strong&gt;lookaheads&lt;/strong&gt;, &lt;strong&gt;lookbehinds&lt;/strong&gt; and &lt;strong&gt;lookarounds&lt;/strong&gt;. But is that a reason not to use them? No! We all use microwaves to make popcorn, but do we care about the physics? I don&amp;#39;t.&lt;/p&gt;
&lt;p&gt;So always remember: you do not need to care about Regular Expression mechanics once you have one&amp;nbsp;that does what you&amp;nbsp;want it to do. So once you have a Regular Expression that splits Hex-Pairs, you are good. Simply use it. &lt;/p&gt;
&lt;p&gt;Instead of spending frustrating hours of crafting complex RegEx patterns, think &amp;quot;code reuse&amp;quot; and visit a search engine like google.Look for &amp;quot;RegEx &amp;lt;Pattern&amp;gt;&amp;quot;, for example &amp;quot;RegEx IPAddress&amp;quot;. Chances are someone else has already done the dirty job for you, and all you need to do is &lt;strong&gt;copy and paste the RegEx pattern&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;If you do want to digest patterns and really understand what is going on, there are plenty of &lt;strong&gt;great RegEx tutorial sites&lt;/strong&gt;. This one explains lookbehinds, lookaheads and lookarounds and helps you understand the previous example: &lt;a target="_blank" href="http://www.regular-expressions.info/lookaround.html"&gt;http://www.regular-expressions.info/lookaround.html&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;What&amp;rsquo;s Next?&lt;/h3&gt;
&lt;p&gt;We covered -match and -split, but there&amp;#39;s a third family member: -replace. So next time we&amp;#39;ll continue talking about RegEx and look how you can replace things, update IP lists and more. Meanwhile, if you have spare time, go visit some of the plenty sites that focus on RegEx, and try and refine your understanding of RegEx placeholders, anchors and quantifiers. Stay tuned!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=13228" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Split" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Split/default.aspx" /><category term="path" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/path/default.aspx" /><category term="Convert" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Convert/default.aspx" /><category term="-match" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/-match/default.aspx" /><category term="Regular Expression" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Regular+Expression/default.aspx" /><category term="RegEx" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/RegEx/default.aspx" /><category term="-split" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/-split/default.aspx" /><category term="splitting" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/splitting/default.aspx" /><category term="hexadecimal" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/hexadecimal/default.aspx" /></entry><entry><title>Regular Expressions Are Your Friend (Part 1)</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/10/27/regular-expressions-are-your-friend-part-1.aspx" /><id>/cs/blogs/tobias/archive/2011/10/27/regular-expressions-are-your-friend-part-1.aspx</id><published>2011-10-27T02:04:00Z</published><updated>2011-10-27T02:04:00Z</updated><content type="html">&lt;p&gt;Yes,&amp;nbsp;&lt;strong&gt;regular expressions&lt;/strong&gt;&amp;nbsp;can be complex but hey no, they do not need to be! You can do amazing things with small and relatively simple regular expressions. So let&amp;rsquo;s take a look at it.&lt;/p&gt;
&lt;h3&gt;Finding Information: -match&lt;/h3&gt;
&lt;p&gt;Often, you need &lt;strong&gt;some piece of information&lt;/strong&gt; that is buried inside of noise text. Let&amp;rsquo;s assume you&amp;rsquo;d like to find the &lt;strong&gt;KB article number&lt;/strong&gt; that is in this text:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text = &amp;#39;The problem was discussed in KB552356 and is solved. KB652534 was not addressed.&amp;#39;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Regular expressions can do this for you. All you need to do is &lt;strong&gt;define the pattern&lt;/strong&gt; you are looking for. Here is a sample pattern that identifies KB article numbers:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $pattern = &amp;#39;KB\d{4,6}&amp;#39;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Before we look at the pattern and how it is constructed, let&amp;rsquo;s see results. PowerShell has an operator called &lt;strong&gt;&amp;ndash;match&lt;/strong&gt; that takes regular expressions and returns &lt;strong&gt;$true&lt;/strong&gt; when it found the pattern in the text:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text -match $pattern&lt;br /&gt;True&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If there was a match, you even get back the expression that matched your pattern in the automatic variable &lt;strong&gt;$matches&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $matches&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Value&lt;br /&gt;----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -----&lt;br /&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KB552356&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; if ($text -match $pattern) { $matches[0] }&lt;br /&gt;KB552356&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;How Patterns Work&lt;/h3&gt;
&lt;p&gt;Regular expression patterns basically contain three things: &lt;strong&gt;anchors, placeholders, and quantifiers&lt;/strong&gt;. In the pattern we used, we had all three elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;KB&amp;rdquo; was an anchor. It expected the pattern to start at the static text &amp;ldquo;KB&amp;rdquo;. &lt;/li&gt;
&lt;li&gt;\d is a placeholder. It represents a digit.&lt;/li&gt;
&lt;li&gt;{4,6} is a quantifier. It expects the placeholder to occur four to six times, so it matches 4 to 6 digits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s not too hard, eh?&lt;/p&gt;
&lt;h3&gt;Creating Groups&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s assume you are just interested in the number of the KB article. To get &lt;strong&gt;just the number&lt;/strong&gt;, you can &lt;strong&gt;create groups&lt;/strong&gt; by using parenthesis.&lt;/p&gt;
&lt;p&gt;Here is the refined pattern:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $pattern = &amp;#39;KB(\d{4,6})&amp;#39;&lt;br /&gt;PS&amp;gt; $text -match $pattern&lt;br /&gt;True&lt;br /&gt;PS&amp;gt; $matches&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Value&lt;br /&gt;----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -----&lt;br /&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 552356&lt;br /&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KB552356&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here, &lt;strong&gt;$matches returns two results&lt;/strong&gt;. Index 0 holds the complete match, and index 1 holds the match of the first group. It contains just the number of the KB article.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; if ($text -match $pattern) { $matches[1] }&lt;br /&gt;552356&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Finding Multiple Matches&lt;/h3&gt;
&lt;p&gt;Unfortunately, &lt;strong&gt;-match only finds the first match&lt;/strong&gt;. That&amp;rsquo;s why it is called &lt;strong&gt;&amp;ndash;match&lt;/strong&gt; and not &lt;strong&gt;&amp;ndash;matches&lt;/strong&gt;. And this is also why it only found the first KB article number in the sample text, not the second one.&lt;/p&gt;
&lt;p&gt;There is a built-in way in PowerShell to find multiple matches, but it is somewhat hidden. &lt;strong&gt;Select-String&lt;/strong&gt; can find multiple matches:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text | Select-String -AllMatches $pattern&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The problem was discussed in KB552356 and is solved. KB652534 was not addressed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;However, the result is the entire text. Only when you poke in the results will you get the individual matches:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text | Select-String -AllMatches $pattern | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value&lt;br /&gt;KB552356&lt;br /&gt;KB652534&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Since that&amp;rsquo;s not really beautiful, you can create your own filter and use it to extract multiple matches:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;filter Get-Matches($Pattern) {&lt;br /&gt;$_ | Select-String -AllMatches $pattern | &lt;br /&gt;&amp;nbsp; Select-Object -ExpandProperty Matches | &lt;br /&gt;&amp;nbsp; Select-Object -ExpandProperty Value &lt;br /&gt;}&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text | Get-Matches $pattern&lt;br /&gt;KB552356&lt;br /&gt;KB652534&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Your filter is pretty flexible. You can use it for example to &lt;strong&gt;parse entire log files&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; Get-Content $env:windir\windowsupdate.log -Encoding UTF8 |&lt;br /&gt;&amp;gt;&amp;gt;&amp;nbsp;&amp;nbsp; Get-Matches &amp;#39;successfully installed.*?update:.*&amp;#39;&lt;br /&gt;&amp;gt;&amp;gt;&lt;br /&gt;successfully installed the following update: Microsoft Silverlight (KB979202)&lt;br /&gt;successfully installed the following update: Security Update for Windows 7 for x64-based&lt;br /&gt;Systems (KB2532531)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(...)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Check the pattern used here:&lt;/p&gt;
&lt;p&gt;We have an &lt;strong&gt;anchor&lt;/strong&gt; (&amp;lsquo;successfully installed&amp;rsquo;), then a &lt;strong&gt;placeholder&lt;/strong&gt; (&amp;lsquo;.&amp;rsquo;=anything) with a &lt;strong&gt;quantifier&lt;/strong&gt; (&amp;lsquo;*?&amp;rsquo; = any number, but as short as possible), then another anchor (&amp;lsquo;update&amp;rsquo;), and then again a placeholder (&amp;lsquo;.&amp;rsquo; = anything) with a quantifier (&amp;lsquo;*&amp;rsquo; = any number).&lt;/p&gt;
&lt;p&gt;Essentially, this extracts all lines from the log file where updates were installed.&lt;/p&gt;
&lt;h3&gt;Finding Multiple Matches Fast&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Select-String&lt;/strong&gt; is rather slow, though. To retrieve multiple matches, &lt;strong&gt;using .NET directly&lt;/strong&gt; speeds up things considerably. Here&amp;rsquo;s the &lt;strong&gt;Get-Matches &lt;/strong&gt;function as a .NET implementation:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;function Get-Matches($Pattern) { &lt;br /&gt;&amp;nbsp; begin { $regex = New-Object Regex($pattern) }&lt;br /&gt;&amp;nbsp; process { foreach ($match in ($regex.Matches($_))) { ([Object[]]$match.Groups)[-1].Value } }&lt;br /&gt;}&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; Get-Content $env:windir\windowsupdate.log -Encoding UTF8 | Get-Matches &amp;#39;successfully installed.*?update:.*&amp;#39;&lt;br /&gt;successfully installed the following update: Microsoft Silverlight (KB979202)&lt;br /&gt;successfully installed the following update: Security Update for Windows 7 for&lt;br /&gt;x64-based Systems (KB2532531)&lt;br /&gt;successfully installed the following update: Update for Windows 7 for x64-based&lt;br /&gt;&amp;nbsp;Systems (KB2533623)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(...)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is not just a lot faster, it &lt;strong&gt;also supports grouping&lt;/strong&gt;, so if you just wanted the product name that was installed, place the last placeholder-expression into parenthesis. &lt;strong&gt;Get-Matches&lt;/strong&gt; always returns the last group:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; Get-Content $env:windir\windowsupdate.log -Encoding UTF8 | Get-Matches &amp;#39;successfully installed.*?update: (.*)&amp;#39;&lt;br /&gt;Microsoft Silverlight (KB979202)&lt;br /&gt;Security Update for Windows 7 for x64-based Systems (KB2532531)&lt;br /&gt;Update for Windows 7 for x64-based Systems (KB2533623)&lt;br /&gt;Security Update for Windows 7 for x64-based Systems (KB2507938)&lt;br /&gt;Security Update for Windows 7 for x64-based Systems (KB2555917)&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Clever RegEx Matching &lt;/h3&gt;
&lt;p&gt;To make life much easier, here&amp;rsquo;s a function that returns regular expression matches as real PowerShell objects:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;function Get-Matches {&lt;br /&gt;&amp;nbsp; param(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Parameter(Mandatory=$true)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $Pattern,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Parameter(ValueFromPipeline=$true)]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $InputObject&lt;br /&gt;&amp;nbsp; )&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;begin {&lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; try {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;$regex = New-Object Regex($pattern) &lt;br /&gt;&amp;nbsp;&amp;nbsp;} &lt;br /&gt;&amp;nbsp;&amp;nbsp;catch {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Throw &amp;quot;Get-Matches: Pattern not correct. &amp;#39;$Pattern&amp;#39; is no valid regular expression.&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;$groups = @($regex.GetGroupNames() | &lt;br /&gt;&amp;nbsp;&amp;nbsp;Where-Object { ($_ -as [Int32]) -eq $null } |&lt;br /&gt;&amp;nbsp;&amp;nbsp;ForEach-Object { $_.toString() })&lt;br /&gt;&amp;nbsp;} &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&amp;nbsp;process { &lt;br /&gt;&amp;nbsp;&amp;nbsp;foreach ($line in $InputObject) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach ($match in ($regex.Matches($line))) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if ($groups.Count -eq 0) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;([Object[]]$match.Groups)[-1].Value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$rv = 1 | Select-Object -Property $groups&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$groups | ForEach-Object {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$rv.$_ = $match.Groups[$_].Value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$rv&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;/em&gt;&lt;em&gt;&amp;nbsp;}&lt;br /&gt;}&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now, if you&amp;rsquo;d like to extract information, you could use it like this:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;quot;KB2786873 good&amp;nbsp; KB27897983&amp;nbsp;&amp;nbsp; bad &amp;quot; | Get-Matches &amp;#39;KB\d{4,8}&amp;#39;&lt;br /&gt;KB2786873&lt;br /&gt;KB27897983&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You could also &lt;strong&gt;create named groups&lt;/strong&gt; which you do by adding &lt;strong&gt;?&amp;lt;Name&amp;gt;&lt;/strong&gt; to your parenthesis. Then, &lt;strong&gt;Get-Matches&lt;/strong&gt; automatically turns these groups into object properties:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; &amp;quot;KB2786873 good&amp;nbsp; KB27897983&amp;nbsp;&amp;nbsp; bad &amp;quot; | Get-Matches &amp;#39;.*?(?&amp;lt;KB&amp;gt;KB\d{4,8}).*?(?&amp;lt;&lt;br /&gt;word&amp;gt;\w+)&amp;#39;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;KB&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;word&lt;br /&gt;--&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&lt;br /&gt;KB2786873&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;good&lt;br /&gt;KB27897983&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bad&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Take a look how easily you now can parse a raw text file and turn it into real PowerShell objects! First read the log file:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;$text = Get-Content $env:windir\windowsupdate.log -Encoding UTF8 -ReadCount 0&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Next,&amp;nbsp; &lt;strong&gt;try a couple of RegEx patterns&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text | Get-Matches &amp;#39;successfully installed.*?update: (.*)&amp;#39;&lt;br /&gt;Microsoft Silverlight (KB979202)&lt;br /&gt;Security Update for Windows 7 for x64-based Systems (KB2532531)&lt;br /&gt;Update for Windows 7 for x64-based Systems (KB2533623)&lt;br /&gt;Security Update for Windows 7 for x64-based Systems (KB2507938)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(...)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text | Get-Matches &amp;#39;successfully installed.*?update: (?&amp;lt;Product&amp;gt;.*)&amp;#39;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Product&lt;br /&gt;-------&lt;br /&gt;Microsoft Silverlight (KB979202)&lt;br /&gt;Security Update for Windows 7 for x64-based Systems (KB2532531)&lt;br /&gt;Update for Windows 7 for x64-based Systems (KB2533623)&lt;br /&gt;Security Update for Windows 7 for x64-based Systems (KB2507938)&lt;br /&gt;Security Update for Windows 7 for x64-based Systems (KB2555917)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(...)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text | Get-Matches &amp;#39;(?&amp;lt;Date&amp;gt;\d{4}-\d{2}-\d{2}).*?(?&amp;lt;Time&amp;gt;\d{2}:\d{2}:\d{2}).*?successfully installed.*?update: (?&amp;lt;Product&amp;gt;.*)&amp;#39;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Date&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Time&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Product&lt;br /&gt;----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -------&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:05:46&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Microsoft Silverlight ...&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update for Wi...&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Update for Windows 7 f...&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update for Wi...&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update for Wi...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(...)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; $text | Get-Matches &amp;#39;(?&amp;lt;Date&amp;gt;\d{4}-\d{2}-\d{2}).*?(?&amp;lt;Time&amp;gt;\d{2}:\d{2}:\d{2}).*?successfully installed.*?update: (?&amp;lt;Product&amp;gt;.*?) \({0,1}KB(?&amp;lt;KB&amp;gt;\d{5,8})&amp;#39;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Date&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Time&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Product&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KB&lt;br /&gt;----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:05:46&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Microsoft Silver... 979202&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update ... 2532531&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Update for Windo... 2533623&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update ... 2507938&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update ... 2555917&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(...)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS&amp;gt; Get-Content $env:windir\windowsupdate.log -Encoding UTF8 -ReadCount 0 | Get-Matches &amp;#39;(?&amp;lt;Date&amp;gt;\d{4}-\d{2}-\d{2}).*?(?&amp;lt;Time&amp;gt;\d{2}:\d{2}:\d{2}).*?successfullyinstalled.*?update: (?&amp;lt;Product&amp;gt;.*?) \({0,1}KB(?&amp;lt;KB&amp;gt;\d{5,8})&amp;#39;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Date&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Time&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Product&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KB&lt;br /&gt;----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -------&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:05:46&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Microsoft Silver... 979202&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update ... 2532531&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Update for Windo... 2533623&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update ... 2507938&lt;br /&gt;2011-10-17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:59:38&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Security Update ... 2555917&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(...)&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Using Special Options&lt;/h3&gt;
&lt;p&gt;There are two ways for you to find matches: use the built-in operator &lt;strong&gt;&amp;ndash;match&lt;/strong&gt;, or use the&lt;strong&gt; .NET Framework RegEx&lt;/strong&gt; type. Generally, you want to use the .NET approach whenever you need to find more than just the first match.&lt;/p&gt;
&lt;p&gt;Depending on which method you choose, there are &lt;strong&gt;different default settings&lt;/strong&gt;, though, that you need to know about. The &lt;strong&gt;&amp;ndash;match&lt;/strong&gt; operator always works case-insensitive whereas .NET always works case-sensitive by default. &lt;/p&gt;
&lt;p&gt;The easiest way to make an expression case-insensitive is to use control operators. This pattern for example works case-sensitive with &lt;strong&gt;&amp;ndash;match&lt;/strong&gt; and case-insensitive with&lt;strong&gt; .NET&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;$pattern = &amp;lsquo;KB\d{6,8}&amp;rsquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This pattern always works case-insensitive, regardless of which method you use:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;$pattern = &amp;lsquo;(?i)KB\d{6,8}&amp;rsquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Another important control operator turns on single-line-mode. Let&amp;rsquo;s assume you downloaded HTML code from a web page and want to find all instances of a pattern. To find all matches, you need to use .NET. To find the pattern in the entire HTML source code, you need single-line-mode.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;To make this work, your pattern should start with &lt;strong&gt;(?si).&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;What&amp;rsquo;s Next?&lt;/h3&gt;
&lt;p&gt;Next week, I&amp;rsquo;ll keep talking about RegEx and look at &lt;strong&gt;splitting&lt;/strong&gt; and &lt;strong&gt;replacing&lt;/strong&gt;. Meanwhile, if you have spare time, go visit some of the plenty sites that focus on RegEx, and try and refine your understanding of RegEx placeholders, anchors and quantifiers. Stay tuned!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=12940" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="-match" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/-match/default.aspx" /><category term="Regular Expression" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Regular+Expression/default.aspx" /><category term="RegEx" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/RegEx/default.aspx" /><category term="Get-Matches" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Get-Matches/default.aspx" /><category term="Pattern" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Pattern/default.aspx" /><category term="Parse" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Parse/default.aspx" /></entry><entry><title>Reuse Your Code - Create Modules Automagically!</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/10/14/reuse-your-code-create-modules-automagically.aspx" /><id>/cs/blogs/tobias/archive/2011/10/14/reuse-your-code-create-modules-automagically.aspx</id><published>2011-10-14T16:34:00Z</published><updated>2011-10-14T16:34:00Z</updated><content type="html">&lt;p&gt;When you write scripts, you want to &lt;strong&gt;automate&lt;/strong&gt; things, fair enough. When it comes to writing scripts, a lot of people do not automate, though. They do the same coding over and over again and waste time and consistency.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s look at some simple code-reusing techniques. It&amp;rsquo;s not that hard at all. In fact, after you read this post you can create your own PowerShell modules in seconds.&lt;/p&gt;
&lt;h3&gt;Code Reuse &amp;ndash; Join The Game!&lt;/h3&gt;
&lt;p&gt;Actually, most PowerShell script writers &lt;strong&gt;reuse code all the time without realizing&amp;nbsp;&amp;ndash; they&amp;rsquo;re just not reusing their own code.&lt;/strong&gt; When you run a &lt;strong&gt;cmdlet&lt;/strong&gt;, you are reusing the code someone else put into that cmdlet. You know that&amp;rsquo;s cool because it makes PowerShell scripts much shorter and easier to write. &lt;/p&gt;
&lt;p&gt;If you want to sort stuff, use &lt;strong&gt;Sort-Object&lt;/strong&gt;. That&amp;#39;s code reuse. In the old days of VBScript you had to re-invent and code bubble-sort-algorithms all the time. No more in PowerShell. If you want to manage Active Directory, import the AD &lt;strong&gt;module&lt;/strong&gt; and use &lt;strong&gt;Get-ADUser&lt;/strong&gt; to search for users. Again, no need for coding tricky AD&amp;nbsp;connect and search stuff.&amp;nbsp;It&amp;#39;s done for you so you can focus on your tasks.&amp;nbsp;&lt;strong&gt;Cmdlets are a big old code reuse party&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Most PowerShell scripters happily use cmdlets but eventually find that they need additional stuff because &lt;strong&gt;there aren&amp;rsquo;t predefined cmdlets for everything&lt;/strong&gt;. So they start scripting day and night and produce long and convoluted scripts that are hard to manage and adjust.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stop that.&lt;/strong&gt; When you need to automate something, grab a coffee and &lt;strong&gt;think first&lt;/strong&gt;. What is the &amp;ldquo;vocabulary&amp;rdquo; (cmdlets, functions) you need to address the problem? Do you have all the vocabulary you need for that? What is missing? Which PowerShell module might already provide the needed vocabulary? There are great modules for VMWare, HyperV, AD, Exchange, SQLServer, you name it.&lt;/p&gt;
&lt;p&gt;Chances are, some vocabulary you need&amp;nbsp;is missing. When that occurs, &lt;strong&gt;design the functions that provide the missing vocabulary first&lt;/strong&gt;. Once they are done, continue with your initial job. Does that take more time? No, it saves time. Here&amp;#39;s how.&lt;/p&gt;
&lt;h3&gt;Adding More Vocabulary - Outputting Data to Excel (for example)&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s say as part of your solution you need a way to output information to &lt;strong&gt;Microsoft Excel&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;Instead of incorporating this functionality&amp;nbsp;into your overall solution (and producing monster scripts), define a function that just focuses on outputting information to Microsoft Excel. Clean and sweet.&amp;nbsp;It could be as simple as this one:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;$env:temp\report$(Get-Date -format yyyyMMddHHmmss).csv&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;[&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;switch&lt;/span&gt;&lt;/span&gt;]&lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$open&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Input&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Export-Csv&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-NoTypeInformation&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-UseCulture&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Encoding&lt;/span&gt;&lt;/span&gt; UTF8&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$open&lt;/span&gt;&lt;/span&gt;) { &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Invoke-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; }&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;Once your function is done, test it. Run it like this:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Process&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt; &amp;ndash;open&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;p&gt;Or like this:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Process&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; Name, Description, Company, MainWindowTitle | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Where-Object&lt;/span&gt;&lt;/span&gt; { &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;MainWindowTitle&lt;/span&gt;&lt;/span&gt; } | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt; &amp;ndash;open&lt;/div&gt;
&lt;h3 class="pscode"&gt;&lt;br /&gt;Making Your Vocabulary Reusable, Too!&lt;/h3&gt;
&lt;p&gt;Once your new function is working like a charm, you could be tempted to copy and paste&amp;nbsp;the code&amp;nbsp;into your overall solution. &lt;strong&gt;Don&amp;rsquo;t do that!&lt;/strong&gt; Don&amp;#39;t assimilate your great new function into some script. Keep it separate! &lt;strong&gt;Make it reusable!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Any script that wants to export data to Microsoft Excel should import the necessary vocabulary from PowerShell modules. Which raises the question: &lt;strong&gt;how would you turn your function into a module?&lt;/strong&gt; That&amp;rsquo;s done automagically when you download, &lt;a target="_blank" href="http://powershell.com/cs/media/p/12823.aspx"&gt;unpack and import this helper module:&lt;/a&gt; &lt;a target="_blank" href="http://powershell.com/cs/media/p/12823.aspx"&gt;http://powershell.com/cs/media/p/12823.aspx&lt;/a&gt;. Simply import it like this:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Import-Module&lt;/span&gt;&lt;/span&gt; c:\pathtomodulefolder\ModuleHelper&lt;/div&gt;
&lt;p&gt;Make sure you specify the path to the unzipped folder named &amp;quot;ModuleHelper&amp;quot; &lt;strong&gt;inside the ZIP&lt;/strong&gt; (due to zipping, there are two nested folders, take the one inside the other), and also note that as a general prerequisite, your PowerShell execution policy should of course allow scripts to run.&lt;/p&gt;
&lt;p&gt;Now, to save your brand new function &lt;strong&gt;Out-ExcelReport&lt;/strong&gt; to a new module of your choice, use the new command &lt;strong&gt;Out-Module&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-Module&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Module&lt;/span&gt;&lt;/span&gt; ReportingStuff &amp;ndash;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;Function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p class="pscode"&gt;That&amp;rsquo;s it! You may have noticed that there was no module &amp;ldquo;ReportingStuff&amp;rdquo; before, but there is now:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Module&lt;/span&gt;&lt;/span&gt; &amp;ndash;ListAvailable&lt;/div&gt;
&lt;p class="pscode"&gt;&lt;br /&gt;So when you import this new module, you load your function &lt;strong&gt;Out-ExcelReport&lt;/strong&gt;. Essentially, you created the same great code reuse mechanism that you enjoyed from other vendors to run AD or Exchange cmdlets. Open a new PowerShell console, and try this:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Import-Module&lt;/span&gt;&lt;/span&gt; ReportingStuff&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Service&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-ExcelReport&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-open&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="modifier"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;p class="pscode"&gt;Your new function &lt;strong&gt;Out-ExcelReport&lt;/strong&gt; just became reusable, and you can import your module in every script that needs your new reporting capabilities now. Never do you have to waste time again to create that functionality again. Cool, eh?&lt;/p&gt;
&lt;h3&gt;Adding and Managing Scripts&lt;/h3&gt;
&lt;p&gt;So far, your new module &amp;ldquo;ReportingStuff&amp;rdquo; has only one function. You can add more, though. Your new module serves as a script repository for you, and &lt;strong&gt;you can save all functions into that module&lt;/strong&gt; that deal with reporting. For example, maybe you&amp;rsquo;d also like to be able to easily create text files that are wide enough to show all results without truncation. Here&amp;rsquo;s the function:&lt;/p&gt;
&lt;div class="pscode"&gt;&amp;lt;&lt;span style="color:#008000;"&gt;&lt;span class="comment"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;SYNOPSIS&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Saves results as text to a text file&lt;br /&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;DESCRIPTION&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Saves results to text file and automatically adjusts the file width so no columns and no data is truncated.&lt;br /&gt;Truncation only occurs for object properties that contain &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;multi-line&lt;/span&gt;&lt;/span&gt; text.&lt;br /&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;PARAMETER&lt;/span&gt;&lt;/span&gt; Path&lt;br /&gt;Optional. Name of text file to create. Existing files will be overwritten. By default, a temporary file &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;in&lt;/span&gt;&lt;/span&gt; the temp folder is created.&lt;br /&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;PARAMETER&lt;/span&gt;&lt;/span&gt; Open&lt;br /&gt;Opens the text file after creation &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;in&lt;/span&gt;&lt;/span&gt; the application that is associated with .&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;txt-files&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;EXAMPLE&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Process&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Where-Object&lt;/span&gt;&lt;/span&gt; { &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;MainWIndowTitle&lt;/span&gt;&lt;/span&gt;} | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Select-Object&lt;/span&gt;&lt;/span&gt; Name, Description, Company, MainWindowTitle , CPU | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-TextReport&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#5f9ea0;"&gt;&lt;span class="modifier"&gt;-Open&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Lists various pieces of information about running processes and writes them to a text file. The file width is automatically adjusted to provide enough room to display all information. The text file then is opened by its default application.&lt;br /&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;EXAMPLE&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-ACL&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$env:windir&lt;/span&gt;&lt;/span&gt; | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-TextReport&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#5f9ea0;"&gt;&lt;span class="modifier"&gt;-open&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Reads the windows folder NTFS permissions and writes the results to a text file. The text file then is opened by its default application.&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#008000;"&gt;&lt;span class="comment"&gt;#&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-TextReport&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Path&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;$env:temp\report.txt&amp;quot;&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;[&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;switch&lt;/span&gt;&lt;/span&gt;]&lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$Open&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Input&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Format-Table&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-AutoSize&lt;/span&gt;&lt;/span&gt; |&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-File&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Width&lt;/span&gt;&lt;/span&gt; 10000&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$open&lt;/span&gt;&lt;/span&gt;) { &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Invoke-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$Path&lt;/span&gt;&lt;/span&gt; }&lt;br /&gt;}&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;br /&gt;
&lt;p&gt;Run it, and test it thoroughly. Once done, again save the function to your module, directly from memory:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-Module&lt;/span&gt;&lt;/span&gt; ReportingTools &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Out-TextReport&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;p&gt;Your new module now has two pieces of vocabulary. To update it, use &lt;strong&gt;&amp;ndash;force&lt;/strong&gt; while you import it: &lt;/p&gt;
&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Import-Module&lt;/span&gt;&lt;/span&gt; ReportingTools &amp;ndash;Force &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Verbose&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h3 class="pscode"&gt;&lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Adding Help&amp;nbsp;and Updating Modules&lt;/h3&gt;
&lt;p&gt;Note that with &lt;strong&gt;Out-TextReport&lt;/strong&gt;, I added a &lt;strong&gt;comment-based help&lt;/strong&gt; block to the code. That&amp;rsquo;s a really good idea because you probably remember how you discovered and learned 3rd party cmdlets &amp;ndash; by using &lt;strong&gt;Get-Help&lt;/strong&gt; and looking at the examples. &lt;/p&gt;
&lt;p&gt;You should provide the same helpful information for the users of your own &amp;ldquo;cmdlets&amp;rdquo;. That&amp;rsquo;s just fair (and gives them an especially professional touch, too). So take a little time and create a great help block with practical examples. &lt;/p&gt;
&lt;p&gt;If you save a function to your module that does not yet have a help block, &lt;strong&gt;Out-Module&lt;/strong&gt; adds one. So when you load and edit functions from your module (hang in, we&amp;rsquo;ll cover that in a second), you just need to fill out the placeholders and save the script.&lt;/p&gt;
&lt;p&gt;You can even &lt;strong&gt;update functions in your module(s)&lt;/strong&gt;. Just assume you wanted to polish a function. Simply open the module path,&amp;nbsp; and load the function into your favorite script editor. Make the adjustments, then save it back:&lt;/p&gt;
&lt;div class="pscode"&gt;Explorer (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-ModulePath&lt;/span&gt;&lt;/span&gt; ReportingTools)&lt;/div&gt;
&lt;p&gt;Likewise, you can send a function that you changed, back to the module by using &lt;strong&gt;Out-Module&lt;/strong&gt; again. It will replace the old version.&lt;/p&gt;
&lt;h3&gt;More to come...&lt;/h3&gt;
&lt;p&gt;At our first &lt;strong&gt;European PowerShell Deep Dive&lt;/strong&gt; yesterday, I also did a presentation about the &lt;strong&gt;power of regular expressions&lt;/strong&gt; and how beautifully PowerShell embraces them. I added all the example functions to a sample module using the technique described above. So next week, I&amp;#39;ll share some serious &lt;strong&gt;RegEx magic&lt;/strong&gt; with you. Stay tuned!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=12822" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Module" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Module/default.aspx" /><category term="Function" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Function/default.aspx" /><category term="Out-TextReport" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Out-TextReport/default.aspx" /><category term="Out-Module" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Out-Module/default.aspx" /><category term="Deep Dive" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Deep+Dive/default.aspx" /><category term="Out-ExcelReport" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Out-ExcelReport/default.aspx" /></entry><entry><title>Applying NTFS Permissions</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/09/21/applying-ntfs-permissions.aspx" /><id>/cs/blogs/tobias/archive/2011/09/21/applying-ntfs-permissions.aspx</id><published>2011-09-21T10:21:00Z</published><updated>2011-09-21T10:21:00Z</updated><content type="html">&lt;p&gt;Recently, I needed to &lt;strong&gt;create a folder with NTFS permissions&lt;/strong&gt;. PowerShell can do that for you, and when you look at your weapons, you&amp;#39;ll find that sometimes it is best to mix command types and not just stick to cmdlets. At the end, I had a handy function that would take a path and a username and do all the tricky NTFS permissions stuff for me. Here is how.&lt;/p&gt;
&lt;h3&gt;Creating Folders&lt;/h3&gt;
&lt;p&gt;Let&amp;#39;s start with the simple part and write a function that creates a folder if that folder does not yet exist:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Create-Folder&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt;) &lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt; ( (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Test-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt;) &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-ne&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$true&lt;/span&gt;&lt;/span&gt;) {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;New-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-ItemType&lt;/span&gt;&lt;/span&gt; Directory | &lt;span style="color:#5f9ea0;"&gt;&lt;span class="verbnoun"&gt;Out-Null&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;br /&gt;}&lt;/div&gt;
&lt;h3&gt;Apply Permissions&lt;/h3&gt;
&lt;p&gt;Next, I want to &lt;strong&gt;add NTFS permissions&lt;/strong&gt; to that folder. A specifc user should get change permission, and all Administrators should get full permission. There are cmdlets to do the job like &lt;strong&gt;Get/Set-ACL&lt;/strong&gt;, but working with them can be hard because they really are just simple wrappers for low-level .NET methods. &lt;/p&gt;
&lt;p&gt;PowerShell is not limited to cmdlets. You can happily use established console-based applications like &lt;strong&gt;cacls.exe&lt;/strong&gt;. Here is an example:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Create-Folder&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt;, &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$user&lt;/span&gt;&lt;/span&gt;) &lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt; ( (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Test-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt;) &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-ne&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$true&lt;/span&gt;&lt;/span&gt;) {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;New-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-ItemType&lt;/span&gt;&lt;/span&gt; Directory | &lt;span style="color:#5f9ea0;"&gt;&lt;span class="verbnoun"&gt;Out-Null&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;CACLS &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;/&lt;/span&gt;&lt;/span&gt;G &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;&amp;quot;Domain Admins&amp;quot;&lt;/span&gt;&lt;/span&gt;:F &lt;br /&gt;CACLS &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;/&lt;/span&gt;&lt;/span&gt;E &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;/&lt;/span&gt;&lt;/span&gt;G &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$user:C&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;As it turns out, this won&amp;#39;t work yet. The first call to &lt;strong&gt;CACLS&lt;/strong&gt; removes existing permissions which is what I want. However, this triggers a confirmation, so I would have to manually &lt;strong&gt;type &amp;quot;Y&amp;quot; to approve&lt;/strong&gt; the operation. The second call to &lt;strong&gt;CACLS&lt;/strong&gt; won&amp;#39;t work at all. &lt;strong&gt;CACLS&lt;/strong&gt; can&amp;#39;t understand it, so it throws back its manual to the caller.&lt;/p&gt;
&lt;p&gt;To correct these issues, &lt;strong&gt;a couple of tricks are needed&lt;/strong&gt;. To automatically send a key to a confirmation, place it on the pipeline. And to resolve the syntax issue, submit arguments to &lt;strong&gt;CACLS&lt;/strong&gt; as a string so PowerShell won&amp;#39;t get confused:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;function&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Create-Folder&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;param&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt;, &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$user&lt;/span&gt;&lt;/span&gt;) &lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; ( (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Test-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt;) &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-ne&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$true&lt;/span&gt;&lt;/span&gt;) {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;New-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$path&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-ItemType&lt;/span&gt;&lt;/span&gt; Directory | &lt;span style="color:#5f9ea0;"&gt;&lt;span class="verbnoun"&gt;Out-Null&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;/span&gt; | CACLS &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;&amp;quot;&amp;quot;$path&amp;quot;&amp;quot; /G &amp;quot;&amp;quot;Domain Admins&amp;quot;&amp;quot;:R&amp;quot;&lt;/span&gt;&lt;/span&gt; | &lt;span style="color:#5f9ea0;"&gt;&lt;span class="verbnoun"&gt;Out-Null&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;CACLS &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;&amp;quot;&amp;quot;$path&amp;quot;&amp;quot; /E /G &amp;quot;&amp;quot;$user&amp;quot;&amp;quot;:F&amp;quot;&lt;/span&gt;&lt;/span&gt; | &lt;span style="color:#5f9ea0;"&gt;&lt;span class="verbnoun"&gt;Out-Null&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;/div&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;Now it is easy to create new folders and apply standard NTFS security:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Create-Folder&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-path&lt;/span&gt;&lt;/span&gt; c:\user1 &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-user&lt;/span&gt;&lt;/span&gt; mydomain\username&lt;/div&gt;
&lt;h3&gt;Careful!&lt;/h3&gt;
&lt;p&gt;Unfortunately, this &lt;strong&gt;approach is not culture-neutral&lt;/strong&gt;. On non-US systems, you probably will want to change two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure you &lt;strong&gt;use the correct name for the Admin group&lt;/strong&gt;. On German systems, it is called &amp;quot;Dom&amp;auml;nen-Admins&amp;quot; instead of &amp;quot;Domain Admins&amp;quot;&lt;/li&gt;
&lt;li&gt;Make sure you &lt;strong&gt;send the correct confirmation key&lt;/strong&gt;. On German systems, it is &amp;quot;J&amp;quot; rather than &amp;quot;Y&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I know that sucks, but for a number of scenarios, incorporating tools like &lt;strong&gt;CACLS&lt;/strong&gt; can simplify life tremendously. Learning points here are: console-based applications are (almost) equal PowerShell citizens. To submit arguments to them, you may have to turn them into a string in order to avoid parsing conflicts.&lt;/p&gt;
&lt;p&gt;See you next time!&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft MVP PowerShell Germany&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=12433" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Folder" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Folder/default.aspx" /><category term="NTFS" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/NTFS/default.aspx" /><category term="get-acl" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/get-acl/default.aspx" /><category term="set-acl" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/set-acl/default.aspx" /><category term="cacls" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/cacls/default.aspx" /></entry><entry><title>Bulk-Renaming Files (and other magic)</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/09/12/bulk-renaming-files-and-other-magic.aspx" /><id>/cs/blogs/tobias/archive/2011/09/12/bulk-renaming-files-and-other-magic.aspx</id><published>2011-09-12T07:49:00Z</published><updated>2011-09-12T07:49:00Z</updated><content type="html">&lt;p&gt;Bulk-renaming files can cause some headache: maybe you&amp;#39;d like to nicely rename all your pictures with a keyword and an incrementing counter, or you&amp;#39;d want to clean up log files and assign them a standard name based on its creation date. It&amp;#39;s easy to do that for one file, and &lt;a target="_blank" href="http://powershell.com/cs/blogs/tips/archive/2011/09/08/bulk-renaming-files.aspx"&gt;with our latest tip&lt;/a&gt;, we illustrated how you can as just as easily do this for hundreds of files, too. Heck, did we get&amp;nbsp;a lot of feedback, both additional questions and great suggestions, so let&amp;#39;s check out what&amp;nbsp;other options you have to bulk-rename files.&lt;/p&gt;
&lt;h3&gt;Creating Filenames with an Incrementing Counter&lt;/h3&gt;
&lt;p&gt;In our original tip, we wanted to rename the content of a folder filled with picture files. Each picture file was supposed to get a new file name like &amp;quot;pictureX&amp;quot;, where &amp;quot;X&amp;quot; was an incrementing counter. Here is the logic:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$global:i&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; 1&lt;/div&gt;
&lt;div class="pscode"&gt;&lt;/div&gt;
&lt;div class="pscode"&gt;dir c:\pictures\ &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;Filter&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;*&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;jpg&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Rename-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-NewName&lt;/span&gt;&lt;/span&gt; { &lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;picture_$i.jpg&amp;quot;&lt;/span&gt;&lt;/span&gt;; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$global:i&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#ff0000;"&gt;&lt;span class="op"&gt;++&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;/div&gt;
&lt;p&gt;There are two things to note here: First, we need a &lt;strong&gt;global variable $i&lt;/strong&gt; which holds the incrementing counter. It needs to be global because we want to remember its setting and increment it with each new file. By default, variables are valid only within their &amp;quot;territory&amp;quot; (scriptblock) and below, so if the counter variable was not global, the scriptblock would always start with a new variable.&lt;/p&gt;
&lt;p&gt;Second, note how &lt;strong&gt;Rename-Item&lt;/strong&gt; accepts a scriptblock. Instead of assigning a static file name, a scriptblock can dynamically create the filename for you. In our example code, the new filename consists of a fixed prefix &amp;quot;picture_&amp;quot; and the counter variable. Pretty nice! But it gets better.&lt;/p&gt;
&lt;h3&gt;Taking Advantage of $_&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Jacques Barathon&lt;/strong&gt; pointed out that the scriptblock that you submit to &lt;strong&gt;Rename-Item&lt;/strong&gt; of course has a &lt;strong&gt;$_ automatic variable&lt;/strong&gt;. This is true for most scriptblocks that you submit to cmdlets. &lt;strong&gt;$_&lt;/strong&gt; always represents the current object you are working with.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s cool because now you can easily use the existing file name and manipulate it. Here is what Jacques did: he wanted to replace a single word in a filename. Let&amp;#39;s check out how he did this:&lt;/p&gt;
&lt;div class="pscode"&gt;dir &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;*&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;docx&lt;/span&gt;&lt;/span&gt; | &lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Rename-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-NewName&lt;/span&gt;&lt;/span&gt; {&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;Name.Replace&lt;/span&gt;&lt;/span&gt;(&amp;lsquo;chapter&amp;rsquo;, &amp;lsquo;chapitre&amp;rsquo;)}&lt;/div&gt;
&lt;p&gt;Whew! That was easy! Since &lt;strong&gt;$_&lt;/strong&gt; is referring to the current file, he accessed the original filename through &lt;strong&gt;$_.Name&lt;/strong&gt;. The result is the filename, and it is a &lt;strong&gt;string&lt;/strong&gt; data type. So he could then use the string method &lt;strong&gt;Replace()&lt;/strong&gt; to replace a word in that filename. The result was the new filename where &amp;#39;chapter&amp;#39; was replaced by &amp;#39;chapitre&amp;#39;. This result was then used by &lt;strong&gt;Rename-Item&lt;/strong&gt; to rename the file.&lt;/p&gt;
&lt;h3&gt;The Sky Is The Limit&lt;/h3&gt;
&lt;p&gt;Of course, &lt;strong&gt;$_&lt;/strong&gt; gives you complete access to &lt;strong&gt;all file properties&lt;/strong&gt;, so you can basically access and use all file properties to construct the new filename. Let&amp;#39;s say you wanted to &lt;strong&gt;replace a filename by a&lt;/strong&gt; &lt;strong&gt;timestamp&lt;/strong&gt; based on the creation date. Here is some code that does it:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$code&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$timestamp&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Date&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;CreationTime&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-format&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800000;"&gt;&lt;span class="string"&gt;&amp;#39;yyyyMMddHHmmssff&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$extension&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;Extension&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$timestamp&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$extension&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;br /&gt;&lt;br /&gt;dir c:\pictures | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Rename-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-NewName&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$code&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;Note how we placed the code (scriptblock) into an extra variable&lt;strong&gt; $code&lt;/strong&gt; to keep the code clean. &lt;/p&gt;
&lt;p&gt;While you are playing with this sample, you&amp;#39;ll definitely run into issues. When doing bulk operations, you always have to take all kinds of &lt;strong&gt;edge cases&lt;/strong&gt; into consideration. &lt;strong&gt;What if there are two files with the very same creation date?&lt;/strong&gt; Since no two files can have the same name, &lt;strong&gt;Rename-Item&lt;/strong&gt; would fail at the second file and not continue. Bummer.&lt;/p&gt;
&lt;p&gt;Happily, you are not bound to any limits. Your scriptblock can contain as much logic as you like. So here is a more advanced approach that takes care of duplicates. It first creates the new filename, then checks if a file with that name already exists, and if so adds a counter to the filename.&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$code&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$timestamp&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Date&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;CreationTime&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-format&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800000;"&gt;&lt;span class="string"&gt;&amp;#39;yyyyMMddHHmmssff&amp;#39;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$extension&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;Extension&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$counter&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; 0&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;while&lt;/span&gt;&lt;/span&gt; (&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$true&lt;/span&gt;&lt;/span&gt;) {&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$filename&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;{0}{1}{2}&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-f&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$timestamp&lt;/span&gt;&lt;/span&gt;, &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$&lt;/span&gt;&lt;/span&gt;( &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$counter&lt;/span&gt;&lt;/span&gt;) { &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;-$counter&amp;quot;&lt;/span&gt;&lt;/span&gt; } &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;else&lt;/span&gt;&lt;/span&gt; { &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt; }) , &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$extension&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$filepath&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Join-Path&lt;/span&gt;&lt;/span&gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Split-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$_&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;FullName&lt;/span&gt;&lt;/span&gt;) &lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$filename&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Test-Path&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$filepath&lt;/span&gt;&lt;/span&gt;) {&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$counter&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#ff0000;"&gt;&lt;span class="op"&gt;++&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;} &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;else&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$filename&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&lt;span class="keyword"&gt;break&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;}&lt;br /&gt;} &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;dir c:\pictures | &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Rename-Item&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-NewName&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$code&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;You can reuse this logic for all events &lt;strong&gt;where you cannot be sure that the filename&lt;/strong&gt; created by your scriptblock &lt;strong&gt;is unique&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s the scoop for today. Hope to see you next week,&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;Microsoft MVP PowerShell Germany&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=12285" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Rename-Item" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Rename-Item/default.aspx" /><category term="Bulk" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Bulk/default.aspx" /></entry><entry><title>Clever Splatting to Pass Optional Parameters</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/08/03/clever-splatting-to-pass-optional-parameters.aspx" /><id>/cs/blogs/tobias/archive/2011/08/03/clever-splatting-to-pass-optional-parameters.aspx</id><published>2011-08-03T04:15:00Z</published><updated>2011-08-03T04:15:00Z</updated><content type="html">&lt;p&gt;Getting system information from WMI is a pretty straight-forward thing with &lt;strong&gt;Get-WMIObject&lt;/strong&gt;. You can even specify one or more computer names or IP addresses to retrieve the information remotely. &lt;strong&gt;That&amp;#39;s all great, but how would you design a function that encapsulates Get-WMIObject (or any other cmdlet that can work both locally and remotely)&lt;/strong&gt;? That&amp;#39;s not so trivial. &lt;/p&gt;
&lt;p&gt;Have a look. There&amp;#39;s a really clever solution for that!&lt;/p&gt;
&lt;h3&gt;Prerequisites: Getting Information Locally or Remotely&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Get-WMIObject&lt;/strong&gt; is a great example of a cmdlet with built-in remoting. For example, to get information about your BIOS, use this call:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WMIObject&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Class&lt;/span&gt;&lt;/span&gt; Win32_BIOS&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SMBIOSBIOSVersion : 02LV.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;MP00&lt;/span&gt;&lt;/span&gt;.20081121.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;hkk&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Manufacturer : Phoenix Technologies Ltd.&lt;br /&gt;Name : Phoenix SecureCore(tm) NB Version 02LV.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;MP00&lt;/span&gt;&lt;/span&gt;.20081121.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;hkk&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;SerialNumber : ZAMA93HS600210&lt;br /&gt;Version : SECCSD &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt; 6040000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WMIObject&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Class&lt;/span&gt;&lt;/span&gt; Win32_BIOS &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-ComputerName&lt;/span&gt;&lt;/span&gt; storage1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SMBIOSBIOSVersion : P03&lt;br /&gt;Manufacturer : Phoenix Technologies LTD&lt;br /&gt;Name : Ver 1.00PARTTBLw&lt;br /&gt;SerialNumber : 98H340ED2H9300237A30A1&lt;br /&gt;Version : PTLTD &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt; 6040000&lt;/div&gt;
&lt;p&gt;As you can see, the cmdlet gets information both from your local system and from one (or more) remote systems - provided they are correctly configured and you have appropriate permissions. It all depends on the parameters you submitted to &lt;strong&gt;Get-WMIObject&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;We are not digging into the security details here because that&amp;#39;s another topic, but if you can&amp;#39;t get &lt;strong&gt;Get-WMIObject&lt;/strong&gt; to work against remote systems, try enabling the &amp;quot;Remote Management Exception&amp;quot; in your firewall when you get &amp;quot;RPC&amp;quot;-exceptions, and use the parameter -credential to log on as a different user when you get &amp;quot;Access Denied&amp;quot;-exceptions.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Creating A (Stupid) Wrapper Function&lt;/h3&gt;
&lt;p&gt;The call to &lt;strong&gt;Get-WMIObject&lt;/strong&gt; is really very trivial, but let&amp;#39;s assume you still &lt;strong&gt;want to encapsulate it and use it as a new function&lt;/strong&gt;. How would you design a function that can run both against the local machine and the remote machine(s)?&lt;/p&gt;
&lt;p&gt;In traditional thinking, you would have to check whether the user has actually submitted the &lt;strong&gt;-computername&lt;/strong&gt; parameter, and then call &lt;strong&gt;Get-WMIObject&lt;/strong&gt; multiple times: if the parameter was submitted, you pass it on to the cmdlet, and if not, you omit it. The result usually is pretty awkward and becomes even more convoluted when you add additional parameters. Here is an &lt;strong&gt;ugly example&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-BIOS&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$computername&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$null&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$credential&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$null&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;&lt;/span&gt; (&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$credential&lt;/span&gt;&lt;/span&gt;) {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WmiObject&lt;/span&gt;&lt;/span&gt; Win32_BIOS &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-computername&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$computername&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-credential&lt;/span&gt;&lt;/span&gt; &lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$credential&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;} &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;elseif&lt;/span&gt;&lt;/span&gt; (&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$computername&lt;/span&gt;&lt;/span&gt;) {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WmiObject&lt;/span&gt;&lt;/span&gt; Win32_BIOS &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-computername&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$computername&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;} &lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;else&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WmiObject&lt;/span&gt;&lt;/span&gt; Win32_BIOS &lt;br /&gt;}&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;At least, it does what it was supposed to do:&lt;/p&gt;
&lt;div class="pscode"&gt;PS&amp;gt; &lt;span style="color:#5f9ea0;"&gt;&lt;span class="verbnoun"&gt;Get-BIOS&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;SMBIOSBIOSVersion : 02LV.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;MP00&lt;/span&gt;&lt;/span&gt;.20081121.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;hkk&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;Manufacturer : Phoenix Technologies Ltd.&lt;br /&gt;Name : Phoenix SecureCore(tm) NB Version 02LV.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;MP00&lt;/span&gt;&lt;/span&gt;.20081121.&lt;span style="color:#8b4513;"&gt;&lt;span class="method"&gt;hkk&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;SerialNumber : ZAMA93HS600210&lt;br /&gt;Version : SECCSD &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt; 6040000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-BIOS&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-computername&lt;/span&gt;&lt;/span&gt; storage1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SMBIOSBIOSVersion : P03&lt;br /&gt;Manufacturer : Phoenix Technologies LTD&lt;br /&gt;Name : Ver 1.00PARTTBLw&lt;br /&gt;SerialNumber : 98H340ED2H9300237A30A1&lt;br /&gt;Version : PTLTD &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt; 6040000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PS&amp;gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-BIOS&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-computername&lt;/span&gt;&lt;/span&gt; storage1 &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-credential&lt;/span&gt;&lt;/span&gt; (&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-Credential&lt;/span&gt;&lt;/span&gt; Administrator)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SMBIOSBIOSVersion : P03&lt;br /&gt;Manufacturer : Phoenix Technologies LTD&lt;br /&gt;Name : Ver 1.00PARTTBLw&lt;br /&gt;SerialNumber : 98H340ED2H9300237A30A1&lt;br /&gt;Version : PTLTD &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;-&lt;/span&gt;&lt;/span&gt; 6040000&lt;/div&gt;
&lt;h3&gt;Create a (Clever) Wrapper Function&lt;/h3&gt;
&lt;p&gt;When you want to &lt;strong&gt;pass optional parameters to cmdlets&lt;/strong&gt;, there is a &lt;strong&gt;much more clever and elegant way&lt;/strong&gt; in PowerShell: &lt;strong&gt;Splatting&lt;/strong&gt;! Instead of manually handing over parameters to a cmdlet, you create a hash table with the parameters you want to pass on. Then, you submit the hash table to the cmdlet.&lt;/p&gt;
&lt;p&gt;The advantage of this not widely known technique is obvious once you see the improved version of the wrapper function: PowerShell already makes available an object that you can use for splatting! The system variable &lt;strong&gt;$PSBoundParameters&lt;/strong&gt; contains all the parameters that were submitted to your function, so whatever someone submitted can be passed on to a cmdlet without the need to check for parameters and call different code:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-BIOS&lt;/span&gt;&lt;/span&gt; {&lt;br /&gt;&lt;span class="keyword"&gt;&lt;span style="color:#0000ff;"&gt;param&lt;/span&gt;&lt;/span&gt;(&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$computername&lt;/span&gt;&lt;/span&gt;,&lt;br /&gt;&lt;span style="color:#800080;"&gt;&lt;span class="var"&gt;$credential&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;Get-WmiObject&lt;/span&gt;&lt;/span&gt; &lt;span class="modifier"&gt;&lt;span style="color:#5f9ea0;"&gt;-Class&lt;/span&gt;&lt;/span&gt; Win32_BIOS @psboundparameters&lt;br /&gt;&lt;br /&gt;}&lt;/div&gt;
&lt;p&gt;Isn&amp;#39;t that a beauty? It works exactly the same, with &lt;em&gt;much&lt;/em&gt; less code. The function calls &lt;strong&gt;Get-WMIObject&lt;/strong&gt; to retrieve WMI BIOS information and optionally passes on the &lt;strong&gt;computername&lt;/strong&gt; and/or &lt;strong&gt;credential&lt;/strong&gt; parameter. No extra work for you anymore.&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;Thanks to&amp;nbsp;Aleksandar and HAL (both PS MVPs) for their valuable input!&lt;/p&gt;
&lt;p&gt;Hope to see you next week,&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;Microsoft MVP PowerShell Germany&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=11692" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Get-WMIObject" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Get-WMIObject/default.aspx" /><category term="Splatting" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Splatting/default.aspx" /><category term="$PSBoundParameters" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/_2400_PSBoundParameters/default.aspx" /></entry><entry><title>Dealing With File Locks</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/06/23/dealing-with-file-locks.aspx" /><id>/cs/blogs/tobias/archive/2011/06/23/dealing-with-file-locks.aspx</id><published>2011-06-23T10:40:00Z</published><updated>2011-06-23T10:40:00Z</updated><content type="html">&lt;p&gt;Sometimes, when you try and access a file, it may refuse to open because it is locked by another user or process. Likewise, you may want to lock a file yourself to make sure the file is not accessed and read/changed while you are manipulating it. Which raises the question: how do you control file locks?&lt;/p&gt;
&lt;h3&gt;Locking a file&lt;/h3&gt;
&lt;p&gt;Get-Content can read text-based files. This cmdlet is not locking the file, so while you are reading it, others can still access or even change it.&lt;/p&gt;
&lt;p&gt;To apply a file lock, you need to use low level .NET methods. The next example illustrates how to open a file with a read/write lock. While this script is running, no one can access the file:&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$file&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; [&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.io.File&lt;/span&gt;&lt;/span&gt;]::&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Open&lt;/span&gt;&lt;/span&gt;(&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;c:\files\somefile.txt&amp;#39;&lt;/span&gt;&lt;/span&gt;, &amp;#39;Open&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;/span&gt;Read&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;/span&gt;None&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;)&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;Read-Host &amp;#39;&lt;/span&gt;&lt;/span&gt;Press ENTER to release file&amp;#39;&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$file&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Close&lt;/span&gt;&lt;/span&gt;()&lt;/div&gt;
&lt;p&gt;The method Open() accepts four arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The path to the file you want to access&lt;/li&gt;
&lt;li&gt;The action you want to take, for example &amp;quot;Open&amp;quot;&lt;/li&gt;
&lt;li&gt;The access you want to use, for example &amp;quot;Read&amp;quot;&lt;/li&gt;
&lt;li&gt;The lock. &amp;quot;None&amp;quot; grants to access to others, so the file is locked. &amp;quot;Read&amp;quot; would allow others to read the file while you are using it, and &amp;quot;ReadWrite&amp;quot; would allow others to read and/or write to the file while you are using it&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Reading File Content&lt;/h3&gt;
&lt;p&gt;When you lock a file,&amp;nbsp;no one else can access it, so you cannot use Get-Content to read its content anymore, either. To read the file content, you would have to refer to .NET methods. Here is an example that reads file content of a&amp;nbsp;file you applied a lock on:&lt;/p&gt;
&lt;p&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$file&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; [&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.io.File&lt;/span&gt;&lt;/span&gt;]::&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Open&lt;/span&gt;&lt;/span&gt;(&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;c:\files\somefile.txt&amp;#39;&lt;/span&gt;&lt;/span&gt;, &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;Open&amp;#39;&lt;/span&gt;&lt;/span&gt;, &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;Read&amp;#39;&lt;/span&gt;&lt;/span&gt;, &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;None&amp;#39;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$reader&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;New-Object&lt;/span&gt;&lt;/span&gt; &lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.IO.StreamReader&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$file&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$text&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$reader&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;ReadToEnd&lt;/span&gt;&lt;/span&gt;()&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$reader&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Close&lt;/span&gt;&lt;/span&gt;()&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$file&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Close&lt;/span&gt;&lt;/span&gt;()&lt;/p&gt;
&lt;h3&gt;Dealing With Existing File Locks&lt;/h3&gt;
&lt;p&gt;When you try and access a file, others may have already applied locks to it. For example, windows maintains a file called $env:windir\windowsupdate.log which carries a &amp;quot;Write&amp;quot; lock: you can read its contents, but you cannot write to it.&lt;/p&gt;
&lt;p&gt;If you tried and accessed that file with a lock of type &amp;quot;None&amp;quot; (exclusive access), your code would fail because the system already implemented a lock which is incompatible to the lock you wanted to apply. You can only lock a file for exclusive access if noone else has applied a&amp;nbsp;lock before.&lt;/p&gt;
&lt;p&gt;To deal with this, you would have to make sure your lock is not conflicting with existing locks. When you specify a &amp;quot;ReadWrite&amp;quot; lock (essentially allowing others to still read and write the file), you can safely read the file despite other locks (except if someone had applied a &amp;quot;None&amp;quot; lock):&lt;/p&gt;
&lt;div class="pscode"&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$file&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; [&lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.io.File&lt;/span&gt;&lt;/span&gt;]::&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Open&lt;/span&gt;&lt;/span&gt;(&lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;quot;$env:windir\windowsupdate.log&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;Open&amp;#39;&lt;/span&gt;&lt;/span&gt;, &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;Read&amp;#39;&lt;/span&gt;&lt;/span&gt;, &lt;span class="string"&gt;&lt;span style="color:#800000;"&gt;&amp;#39;ReadWrite&amp;#39;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$reader&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="verbnoun"&gt;&lt;span style="color:#5f9ea0;"&gt;New-Object&lt;/span&gt;&lt;/span&gt; &lt;span class="namespace"&gt;&lt;span style="color:#8b4513;"&gt;System.IO.StreamReader&lt;/span&gt;&lt;/span&gt;(&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$file&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$text&lt;/span&gt;&lt;/span&gt; &lt;span class="op"&gt;&lt;span style="color:#ff0000;"&gt;=&lt;/span&gt;&lt;/span&gt; &lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$reader&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;ReadToEnd&lt;/span&gt;&lt;/span&gt;()&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$reader&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Close&lt;/span&gt;&lt;/span&gt;()&lt;br /&gt;&lt;span class="var"&gt;&lt;span style="color:#800080;"&gt;$file&lt;/span&gt;&lt;/span&gt;.&lt;span class="method"&gt;&lt;span style="color:#8b4513;"&gt;Close&lt;/span&gt;&lt;/span&gt;()&lt;/div&gt;
&lt;p&gt;This is how Get-Content works. It uses a &amp;quot;ReadWrite&amp;quot; lock. The above low level .NET access is a lot faster though because it is not reading the file line by line. Instead, it returns the entire file content as one (large) string.&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;Hope to see you next week,&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;Microsoft MVP PowerShell Germany&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=10975" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Lock" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Lock/default.aspx" /><category term="None" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/None/default.aspx" /><category term="ReadWrite" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/ReadWrite/default.aspx" /><category term="File" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/File/default.aspx" /><category term="Open" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Open/default.aspx" /></entry><entry><title>Renaming Files Puzzle - And Three Golden PowerShell Rules</title><link rel="alternate" type="text/html" href="/cs/blogs/tobias/archive/2011/05/23/renaming-files-puzzle-and-three-golden-powershell-rules.aspx" /><id>/cs/blogs/tobias/archive/2011/05/23/renaming-files-puzzle-and-three-golden-powershell-rules.aspx</id><published>2011-05-23T11:57:00Z</published><updated>2011-05-23T11:57:00Z</updated><content type="html">&lt;p&gt;I am moderator for one of our &lt;a target="_blank" href="http://powershell.com/cs/forums/200.aspx"&gt;Ask-the-Experts forums&lt;/a&gt;, and every once in a while, I get questions I would like to discuss. Like this one:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;&amp;quot;I am tring to write a quick interactive command that will rename all the jpgs in the current directory and came up with the following command:&lt;br /&gt;&lt;br /&gt;$i=1; gci | ? { ($_.Extension -eq &amp;quot;.JPG&amp;quot;) -and !($_.Name.StartsWith(&amp;quot;cover&amp;quot;)) } | % { Rename-Item -Path &amp;quot;.\$_&amp;quot; -NewName [string]::Format(&amp;quot;picture_$i.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div id=":1dv"&gt;&lt;em&gt;&lt;strong&gt;jpg&amp;quot;); $i++ }&lt;br /&gt;&lt;br /&gt;There appears to be a parameter binding issue. But the 2 parameters I have specified (-Path and -NewName) I have named so I assumed that position is not of relevance. What am I missing here?&amp;quot;&lt;/strong&gt;&lt;/em&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p&gt;Let&amp;#39;s see how we can solve this puzzle and come up with an easy solution using the&amp;nbsp;&amp;quot;3 Golden PowerShell rules&amp;quot;!&lt;/p&gt;
&lt;h2&gt;Think PowerShell! Use Parameters!&lt;/h2&gt;
&lt;div&gt;
&lt;p&gt;Whenever you work with files, &lt;strong&gt;Get-Childitem&lt;/strong&gt; (aka &lt;strong&gt;dir&lt;/strong&gt; or &lt;strong&gt;ls&lt;/strong&gt;) is your friend. And like most &lt;strong&gt;Get-*&lt;/strong&gt; cmdlets, it has a lot of parameters to help you get what you want. Of course you can always use &lt;strong&gt;Where-Object&lt;/strong&gt; (short: &lt;strong&gt;?&lt;/strong&gt;) to filter the results once you got them. That is only your last resort, though. Before you do that, always make sure the original cmdlet has no way of filtering. And &lt;strong&gt;Get-Childitem&lt;/strong&gt; has. So the first part would be selecting the files you want to rename like this:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Get-Childitem c:\somefolder -Filter cover*.jpg&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Another thing to watch out is this: never use &lt;strong&gt;Get-Childitem&lt;/strong&gt; without specifying an absolute or relative path to start with. Else, it will depend on your current path, and that path may change. So the next time you are running your code, it may no longer work because your current path is no longer pointing to the files you want to rename.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rule #1:&lt;/strong&gt; Before you resort to &lt;strong&gt;Where-Object&lt;/strong&gt;, make sure the &lt;em&gt;upstream&lt;/em&gt; cmdlet has no built-in parameters to do the same! It is easier and much faster!&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Think PowerShell! Use Pipeline Binding!&lt;/h2&gt;
&lt;p&gt;The next thing is finding the right cmdlet for the action you want to take. &lt;strong&gt;Rename-Item&lt;/strong&gt; was the perfect choice. However, it is not necessary to use &lt;strong&gt;Foreach-Object&lt;/strong&gt; (short: &lt;strong&gt;%&lt;/strong&gt;) to iterate through all the files. Instead, &lt;strong&gt;Rename-Item&lt;/strong&gt; is able to accept the files directly via pipeline. &lt;strong&gt;Get-Help&lt;/strong&gt; can tell you that:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Get-Help -Name Rename-Item -Parameter Path&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It returns: &lt;em&gt;Accept pipeline input?&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; true (ByValue, ByPropertyName) &lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So the second part of the solution looks like this:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Get-Childitem c:\somefolder -Filter cover*.jpg | Rename-Item -NewName &amp;quot;somenewname.jpg&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rule #2:&lt;/strong&gt; Before you resport to &lt;strong&gt;Foreach-Object&lt;/strong&gt;, make sure the &lt;em&gt;downstream&lt;/em&gt; cmdlet cannot receive your results via pipeline!&lt;/p&gt;
&lt;h2&gt;Think PowerShell! Use Scriptblocks!&lt;/h2&gt;
&lt;p&gt;Of course, renaming a bunch of files to the exact same name will fail, and thus you may be intrigued to use a &lt;strong&gt;foreach-object&lt;/strong&gt; loop after all so you can compose individual file names. But that is not necessary. Many cmdlets accept &lt;strong&gt;scriptblocks&lt;/strong&gt; which resemble executable code, so rather than submitting a fixed name, submit code that composes the name. The final solution looks like this:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;$global:i = 1; dir c:\test1\ -filter cover*.bmp | rename-item -NewName { &amp;quot;picture_$i.jpg&amp;quot;; $global:i++}&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This line will rename all jpg files that start with &amp;quot;cover&amp;quot; in folder c:\test1 to pictureX.jpg where X is an incrementing index number.&lt;/p&gt;
&lt;p&gt;Note the use of &lt;strong&gt;global:&lt;/strong&gt;&amp;nbsp; (you could also use &lt;strong&gt;script:&lt;/strong&gt;). With this prefix, you tell PowerShell to use the same variable. If you omitted that, incrementing $i inside the scriptblock would not have an effect on the global variable $i, because inside a script block, the variable is local and would be discarded each time after the script block was done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rule #3:&lt;/strong&gt;&amp;nbsp;Before you design complex code to calculate results for parameters, make sure the parameters do not accept scriptblocks in the first place.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;Admittedly, this third rule is not that easy to follow because there is no easy way to know whether a parameter can actually receive a script block. &lt;/div&gt;
&lt;div&gt;
&lt;p&gt;Hope to see you next week,&lt;/p&gt;
&lt;p&gt;Tobias&lt;/p&gt;
&lt;p&gt;Microsoft MVP PowerShell Germany&lt;/p&gt;
&lt;p&gt;P.S.&lt;br /&gt;If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here&amp;#39;s how to get in touch with me: &lt;a href="mailto:tobias.weltner@scriptinternals.de"&gt;&lt;span style="color:#3366cc;"&gt;tobias.weltner@scriptinternals.de&lt;/span&gt;&lt;/a&gt;&amp;nbsp; &lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=10492" width="1" height="1"&gt;</content><author><name>Tobias</name><uri>http://powershell.com/cs/members/Tobias/default.aspx</uri></author><category term="Where-Object" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Where-Object/default.aspx" /><category term="Pipeline" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/Pipeline/default.aspx" /><category term="scriptblock" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/scriptblock/default.aspx" /><category term="golden rule" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/golden+rule/default.aspx" /><category term="foreach-object" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/foreach-object/default.aspx" /><category term="rename" scheme="http://powershell.com/cs/blogs/tobias/archive/tags/rename/default.aspx" /></entry></feed>
