<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Posts about Windows</title><link>https://chriswarrick.com/</link><atom:link href="https://chriswarrick.com/blog/tags/windows.xml" rel="self" type="application/rss+xml" /><description>A rarely updated blog, mostly about programming.</description><lastBuildDate>Fri, 06 Mar 2026 19:00:00 GMT</lastBuildDate><generator>https://github.com/Kwpolska/YetAnotherBlogGenerator</generator><item><title>Docker In WSL: A No-Frills Guide</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/</link><pubDate>Wed, 04 Mar 2026 17:20:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/</guid><description>Docker is great, but how do you run Linux containers on Windows? In WSL, of course. The Docker Desktop application is certainly an option, but it eats up a lot of RAM thanks to its Electron-based GUI. It also has an LLM chatbot (because of course it does, it’s 2026 after all). Just the Docker Desktop installer takes up 598 MB. What if you want something simpler and with less RAM consumption? WSL 2 makes it really straightforward to set up Docker with some basic Windows integration.
</description><content:encoded><![CDATA[<p>Docker is great, but how do you run Linux containers on Windows? In WSL, of course. The Docker Desktop application is certainly an option, but it eats up a lot of RAM thanks to its Electron-based GUI. It also has an LLM chatbot (because of course it does, it’s 2026 after all). Just the Docker Desktop installer takes up 598 MB. What if you want something simpler and with less RAM consumption? WSL 2 makes it really straightforward to set up Docker with some basic Windows integration.</p>



<p>(Also, Docker Desktop demands payment for enterprise use; if your company fits the enterprise criteria but cannot afford $16/developer/month, run.)</p>
<p><em>Console command conventions: <code>$</code> means Linux, <code>&gt;</code> means Windows (PowerShell).<br>Path conventions: <code>/</code> means Linux, <code>\</code> means Windows.</em></p>
<h2 id="linux-setup">Linux setup</h2>
<p>You need to have WSL and a distribution installed, and you must be using WSL 2. <a href="https://learn.microsoft.com/en-us/windows/wsl/install">Read the install docs if you need to do it.</a></p>
<p>➡️ Start by installing Docker in the usual way for your Linux distribution of choice. I’m using Ubuntu with <a href="https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository">the official Docker apt repository</a>. Make sure to <a href="https://docs.docker.com/engine/install/linux-postinstall">add yourself to the Docker group</a>.</p>
<p>➡️ Enable systemd support in WSL. Put those two lines into <code>/etc/wsl.conf</code>:</p>
<div class="highlight"><pre><span></span><span class="k">[boot]</span>
<span class="na">systemd</span><span class="o">=</span><span class="s">true</span>
</pre></div>

<p>You can do it like so (if you don’t have a <code>wsl.conf</code> file already):</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span><span class="nb">printf</span><span class="w"> </span><span class="s1">&#39;[boot]\nsystemd=true\n&#39;</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>sudo<span class="w"> </span>tee<span class="w"> </span>/etc/wsl.conf
</pre></div>

<p>➡️ Next, enable the systemd services:</p>
<div class="highlight"><pre><span></span><span class="gp">$ </span>sudo<span class="w"> </span>systemctl<span class="w"> </span><span class="nb">enable</span><span class="w"> </span>containerd.service
<span class="gp">$ </span>sudo<span class="w"> </span>systemctl<span class="w"> </span><span class="nb">enable</span><span class="w"> </span>docker.service
</pre></div>

<h2 id="exposing-docker-to-windows">Exposing Docker to Windows</h2>
<p>By default, Docker only listens on a Unix domain socket. To work with Docker from Windows, it needs to listen on a TCP socket. If you’re fine with using Docker only from the WSL terminal, and won’t run any Docker-compatible tools on the Windows side, you can basically stop reading here.</p>
<p>Adding a socket can be done in two ways: by adding command-line arguments, or by modifying <code>/etc/docker/daemon.json</code>. If your Docker systemd service is passing in arguments, you need to edit the service, and can’t do it using the <code>daemon.json</code> file. Let’s edit the service, as this is more likely to be what you need.</p>
<p>➡️ Run <code>sudo systemctl edit docker.service</code> and override the <code>ExecStart</code> command. The first <code>ExecStart=</code> line clears out the existing configuration defined in the system-provided service. The second line is the new command. To avoid breaking things, review the <code>ExecStart</code> command that is shown as the current content of your <code>docker.service</code>, and make sure you have both <code>-H</code> arguments as in my example below.</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-1" name="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-1"></a><span class="c1">### Editing /etc/systemd/system/docker.service.d/override.conf</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-2" name="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-2"></a><span class="c1">### Anything between here and the comment below will become the contents of the drop-in file</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-3" name="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-3"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-4" name="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-4"></a><span class="k">[Service]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-5" name="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-5"></a><span class="na">ExecStart</span><span class="o">=</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-6"><code data-line-number="6"></code></a></td><td class="code"><code><a id="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-6" name="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-6"></a><span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375 --containerd=/run/containerd/containerd.sock</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-7"><code data-line-number="7"></code></a></td><td class="code"><code><a id="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-7" name="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-7"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-8"><code data-line-number="8"></code></a></td><td class="code"><code><a id="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-8" name="code_74e3f0c1e66ee254ad53204ffcc30ad66875b005-8"></a><span class="c1">### Edits below this comment will be discarded</span>
</code></td></tr></table></div>
<p>➡️ We’re done with the WSL setup, so let’s restart WSL and make sure Docker is working:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-1" name="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-1"></a>&gt; wsl --shutdown
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-2" name="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-2"></a>&gt; wsl docker run --rm hello-world
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-3" name="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-3"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-4" name="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-4"></a>Hello from Docker!
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-5" name="code_2c45ca58d73b1fb5d17bb46934173dcfb55b65e3-5"></a>This message shows that your installation appears to be working correctly.
</code></td></tr></table></div>
<h2 id="windows-setup">Windows setup</h2>
<p>➡️ Download the latest version of the <a href="https://download.docker.com/win/static/stable/x86_64/">Docker binaries</a>. Extract <code>docker.exe</code> and put it somewhere on your Windows <code>PATH</code>. I have a <code>~\Tools\bin</code> folder for things like that.</p>
<p>We need to tell the Docker CLI where to look for Docker. The easiest way to do this is to set the <code>DOCKER_HOST</code> environment variable. This might not be the cleanest way if you want to work with Windows containers or Docker Desktop, but you don’t, so we don’t need to bother with anything nicer.</p>
<p>➡️ Press the Start key, type <em>environment variables</em> (or the equivalent phrase in your Windows language) and open the environment variables editor. Add an environment variable named <code>DOCKER_HOST</code> with the value <code>tcp://127.0.0.1:2375</code>.</p>
<p>➡️ Restart your Terminal. In one tab, run a WSL shell. In another, run PowerShell (or cmd):</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-1" name="code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-1"></a>&gt; docker run -it --rm hello-world
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-2" name="code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-2"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-3" name="code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-3"></a>Hello from Docker!
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-4" name="code_71178a5b7990c2f9c7621fcb3b389c2f84e8c174-4"></a>This message shows that your installation appears to be working correctly.
</code></td></tr></table></div>
<h2 id="optional-docker-compose">Optional: Docker Compose</h2>
<p>Docker Compose is a plugin for Docker CLI, and it not included in the Docker binary we’ve installed on Windows. It is quite easy to add it in if you want.</p>
<p>➡️ Download the latest version of the <a href="https://github.com/docker/compose/releases/latest">Docker Compose binary</a> (<code>docker-compose-windows-x86_64.exe</code>). Move it to <code>~\.docker\cli-plugins\docker-compose.exe</code>.</p>
<div class="highlight"><pre><span></span>&gt; mkdir ~\.docker\cli-plugins
&gt; mv ~\Downloads\docker-compose-windows-x86_64.exe ~\.docker\cli-plugins\docker-compose.exe
</pre></div>

<p>Let’s test it out:</p>
<div class="highlight"><pre><span></span>&gt; docker compose version
Docker Compose version v5.1.0
</pre></div>

<h2 id="optional-wsl-vm-management">Optional: WSL VM management</h2>
<p>WSL does not automatically start the Linux VM. And it kills it if no user process is running inside of it.</p>
<p>If you’re fine with having a WSL terminal open while you’re working with Docker, you don’t need to do anything. But if you don’t want to see a WSL terminal, <a href="https://askubuntu.com/questions/1177273/is-there-an-easy-way-to-have-wsl-ubuntu-services-start-automatically-on-windows">make Windows start WSL when you log in</a> and set a really large <em>VM Idle Timeout</em> in the <em>Optional Features</em> tab of the <em>WSL Settings</em> app. The maximum value accepted by the GUI<a id="fnref:1" href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#fn:1" class="footnote-ref"><sup>1</sup></a> is the maximum value a 32-bit integer can represent, i.e. 2147483647 ms (24 days). You can also manually add the option to <code>~\.wslconfig</code>:</p>
<div class="highlight"><pre><span></span><span class="k">[wsl2]</span>
<span class="na">vmIdleTimeout</span><span class="o">=</span><span class="s">2147483647</span>
</pre></div>

<div class="footnotes">
<hr>
<ol>
<li id="fn:1">
<p>If you try to input a larger number, the app just crashes. That’s what you get when you fire your QA department and let AI take over coding.<a href="https://chriswarrick.com/blog/2026/03/04/docker-in-wsl-a-no-frills-guide/#fnref:1" class="footnote-back-ref">&#8617;</a></p>
</li>
</ol>
</div>
]]></content:encoded><category>Programming</category><category>Docker</category><category>Linux</category><category>Windows</category><category>WSL</category></item><item><title>PowerShell: the object-oriented shell you didn’t know you needed</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/</link><pubDate>Mon, 29 Apr 2024 16:45:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/</guid><description>PowerShell is an interactive shell and scripting language from Microsoft. It’s object-oriented — and that’s not just a buzzword, that’s a big difference to how the standard Unix shells work. And it is actually usable as an interactive shell.
</description><content:encoded><![CDATA[<p>PowerShell is an interactive shell and scripting language from Microsoft. It’s object-oriented — and that’s not just a buzzword, that’s a big difference to how the standard Unix shells work. And it is actually usable as an interactive shell.</p>



<h2 id="getting-started">Getting Started</h2>
<p>PowerShell is so nice, Microsoft made it twice.</p>
<p>Specifically, there concurrently exist two products named PowerShell:</p>
<ul>
<li>Windows PowerShell (5.1) is a built-in component of Windows. It is proprietary, Windows-only, and is based on the equally proprietary and equally Windows-only .NET Framework 4.x. It has a blue icon.</li>
<li>PowerShell (7.x), formerly known as PowerShell Core, is a stand-alone application. It is MIT-licensed <a href="https://github.com/PowerShell/PowerShell">(developed on GitHub)</a>, available for Windows, Linux, and macOS, and is based on the equally MIT-licensed and equally multi-platform .NET (formerly .NET Core). It has a black icon.</li>
</ul>
<p>Windows PowerShell development stopped when PowerShell (Core) came out. There are some niceties and commands missing in it, but it is still a fine option for trying it out or for when one can’t install PowerShell on a Windows system but need to solve something with code.</p>
<p>All examples in this post should work in either version of PowerShell on any OS (unless explicitly noted otherwise).</p>
<p>Install the modern PowerShell: <a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.4">Windows</a>, <a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux?view=powershell-7.4">Linux</a>, <a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux?view=powershell-7.4">macOS</a>.</p>
<h2 id="objects-in-my-shell">Objects? In my shell?</h2>
<p>Let’s try getting a directory listing. This is Microsoft land, so let’s try the DOS command for a directory listing — that would be <code>dir</code>:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-1" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-1"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="nb">dir</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-2" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-2"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-3" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-3"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Directory</span><span class="p">:</span> <span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-4" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-4"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-5" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-5"></a><span class="n">Mode</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">LastWriteTime</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Length</span> <span class="n">Name</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-6" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-6"></a><span class="p">----</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">-------------</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">------</span> <span class="p">----</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-7" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-7"></a><span class="n">d</span><span class="p">----</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2024</span><span class="p">-</span><span class="n">04</span><span class="p">-</span><span class="n">29</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">18</span><span class="p">:</span><span class="n">00</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">world</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-8" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-8"></a><span class="n">-a</span><span class="p">---</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2024</span><span class="p">-</span><span class="n">04</span><span class="p">-</span><span class="n">29</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">18</span><span class="p">:</span><span class="n">00</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">23</span> <span class="n">example</span><span class="p">.</span><span class="n">py</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-9" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-9"></a><span class="n">-a</span><span class="p">---</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2024</span><span class="p">-</span><span class="n">04</span><span class="p">-</span><span class="n">29</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">18</span><span class="p">:</span><span class="n">00</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">7</span> <span class="n">foobar</span><span class="p">.</span><span class="n">txt</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-10" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-10"></a><span class="n">-a</span><span class="p">---</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2024</span><span class="p">-</span><span class="n">04</span><span class="p">-</span><span class="n">29</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">18</span><span class="p">:</span><span class="n">00</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">14</span> <span class="n">helloworld</span><span class="p">.</span><span class="n">txt</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-11" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-11"></a><span class="n">-a</span><span class="p">---</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2024</span><span class="p">-</span><span class="n">04</span><span class="p">-</span><span class="n">29</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">18</span><span class="p">:</span><span class="n">00</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">0</span> <span class="n">newfile</span><span class="p">.</span><span class="n">txt</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_5487e1609201ce902c883e7cd5ea641dfe46849f-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="code_5487e1609201ce902c883e7cd5ea641dfe46849f-12" name="code_5487e1609201ce902c883e7cd5ea641dfe46849f-12"></a><span class="n">-a</span><span class="p">---</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2024</span><span class="p">-</span><span class="n">04</span><span class="p">-</span><span class="n">29</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">18</span><span class="p">:</span><span class="n">00</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">5</span> <span class="n">test</span><span class="p">.</span><span class="n">txt</span>
</code></td></tr></table></div>
<p>This looks like a typical (if slightly verbose) file listing.</p>
<p>Now, let’s try to do something useful with this. Let’s get the total size of all <code>.txt</code> files.</p>
<p>In a Unix shell, one option is <code>du -bc *.txt</code>. The arguments: <code>-b</code> (<code>--bytes</code>) gives the real byte size, and <code>-c</code> (<code>--summarize</code>) produces a total. The result is this:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2026e78c6a1fda66be195947828c8cd5292e1e12-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="code_2026e78c6a1fda66be195947828c8cd5292e1e12-1" name="code_2026e78c6a1fda66be195947828c8cd5292e1e12-1"></a>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foobar.txt
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2026e78c6a1fda66be195947828c8cd5292e1e12-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="code_2026e78c6a1fda66be195947828c8cd5292e1e12-2" name="code_2026e78c6a1fda66be195947828c8cd5292e1e12-2"></a>14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;helloworld.txt
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2026e78c6a1fda66be195947828c8cd5292e1e12-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="code_2026e78c6a1fda66be195947828c8cd5292e1e12-3" name="code_2026e78c6a1fda66be195947828c8cd5292e1e12-3"></a>0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newfile.txt
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2026e78c6a1fda66be195947828c8cd5292e1e12-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="code_2026e78c6a1fda66be195947828c8cd5292e1e12-4" name="code_2026e78c6a1fda66be195947828c8cd5292e1e12-4"></a>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.txt
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2026e78c6a1fda66be195947828c8cd5292e1e12-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="code_2026e78c6a1fda66be195947828c8cd5292e1e12-5" name="code_2026e78c6a1fda66be195947828c8cd5292e1e12-5"></a>26&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total
</code></td></tr></table></div>
<p>But how to get just the number? This requires text manipulation (getting the first word of the last line). Something like <code>du -bc *.txt | tail -n 1 | cut -f 1</code> will do. There’s also <code>wc --total=only --bytes *.txt</code> — but this is specific to GNU wc, so it won’t cut it on *BSD or macOS. Another option would be to parse the output of <code>ls -l</code> — but that might not always be easy, and the output may contain something unexpected added by the specific <code>ls</code> version or the user’s specific shell configuration.</p>
<p>Let’s try something in PowerShell. If we do <code>$x = dir</code>, we’ll have the output of the <code>dir</code> command in <code>$x</code>. Let’s try to analyse it further, is the first character a newline?</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_732adae82ccca20456365dc143dee4ccde7d735d-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="code_732adae82ccca20456365dc143dee4ccde7d735d-1" name="code_732adae82ccca20456365dc143dee4ccde7d735d-1"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="nv">$x</span> <span class="p">=</span> <span class="nb">dir</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_732adae82ccca20456365dc143dee4ccde7d735d-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="code_732adae82ccca20456365dc143dee4ccde7d735d-2" name="code_732adae82ccca20456365dc143dee4ccde7d735d-2"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="nv">$x</span><span class="p">[</span><span class="n">0</span><span class="p">]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_732adae82ccca20456365dc143dee4ccde7d735d-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="code_732adae82ccca20456365dc143dee4ccde7d735d-3" name="code_732adae82ccca20456365dc143dee4ccde7d735d-3"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_732adae82ccca20456365dc143dee4ccde7d735d-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="code_732adae82ccca20456365dc143dee4ccde7d735d-4" name="code_732adae82ccca20456365dc143dee4ccde7d735d-4"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Directory</span><span class="p">:</span> <span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_732adae82ccca20456365dc143dee4ccde7d735d-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="code_732adae82ccca20456365dc143dee4ccde7d735d-5" name="code_732adae82ccca20456365dc143dee4ccde7d735d-5"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_732adae82ccca20456365dc143dee4ccde7d735d-6"><code data-line-number="6"></code></a></td><td class="code"><code><a id="code_732adae82ccca20456365dc143dee4ccde7d735d-6" name="code_732adae82ccca20456365dc143dee4ccde7d735d-6"></a><span class="n">Mode</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">LastWriteTime</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Length</span> <span class="n">Name</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_732adae82ccca20456365dc143dee4ccde7d735d-7"><code data-line-number="7"></code></a></td><td class="code"><code><a id="code_732adae82ccca20456365dc143dee4ccde7d735d-7" name="code_732adae82ccca20456365dc143dee4ccde7d735d-7"></a><span class="p">----</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">-------------</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">------</span> <span class="p">----</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_732adae82ccca20456365dc143dee4ccde7d735d-8"><code data-line-number="8"></code></a></td><td class="code"><code><a id="code_732adae82ccca20456365dc143dee4ccde7d735d-8" name="code_732adae82ccca20456365dc143dee4ccde7d735d-8"></a><span class="n">d</span><span class="p">----</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2024</span><span class="p">-</span><span class="n">04</span><span class="p">-</span><span class="n">29</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">18</span><span class="p">:</span><span class="n">00</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">world</span>
</code></td></tr></table></div>
<p>That’s interesting, we didn’t get the first character or the first line, we got the first <em>file</em>. And if we try <code>$x[1]</code>?</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a0872bfab1d587bc08f128eb0a76b8101473a096-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="code_a0872bfab1d587bc08f128eb0a76b8101473a096-1" name="code_a0872bfab1d587bc08f128eb0a76b8101473a096-1"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="nv">$x</span><span class="p">[</span><span class="n">1</span><span class="p">]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a0872bfab1d587bc08f128eb0a76b8101473a096-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="code_a0872bfab1d587bc08f128eb0a76b8101473a096-2" name="code_a0872bfab1d587bc08f128eb0a76b8101473a096-2"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a0872bfab1d587bc08f128eb0a76b8101473a096-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="code_a0872bfab1d587bc08f128eb0a76b8101473a096-3" name="code_a0872bfab1d587bc08f128eb0a76b8101473a096-3"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Directory</span><span class="p">:</span> <span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a0872bfab1d587bc08f128eb0a76b8101473a096-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="code_a0872bfab1d587bc08f128eb0a76b8101473a096-4" name="code_a0872bfab1d587bc08f128eb0a76b8101473a096-4"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a0872bfab1d587bc08f128eb0a76b8101473a096-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="code_a0872bfab1d587bc08f128eb0a76b8101473a096-5" name="code_a0872bfab1d587bc08f128eb0a76b8101473a096-5"></a><span class="n">Mode</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">LastWriteTime</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Length</span> <span class="n">Name</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a0872bfab1d587bc08f128eb0a76b8101473a096-6"><code data-line-number="6"></code></a></td><td class="code"><code><a id="code_a0872bfab1d587bc08f128eb0a76b8101473a096-6" name="code_a0872bfab1d587bc08f128eb0a76b8101473a096-6"></a><span class="p">----</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">-------------</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">------</span> <span class="p">----</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a0872bfab1d587bc08f128eb0a76b8101473a096-7"><code data-line-number="7"></code></a></td><td class="code"><code><a id="code_a0872bfab1d587bc08f128eb0a76b8101473a096-7" name="code_a0872bfab1d587bc08f128eb0a76b8101473a096-7"></a><span class="n">-a</span><span class="p">---</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2024</span><span class="p">-</span><span class="n">04</span><span class="p">-</span><span class="n">29</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">18</span><span class="p">:</span><span class="n">00</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">23</span> <span class="n">example</span><span class="p">.</span><span class="n">py</span>
</code></td></tr></table></div>
<p>What if we try getting the <code>Length</code> property out of that?</p>
<div class="highlight"><pre><span></span><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="nv">$x</span><span class="p">[</span><span class="n">1</span><span class="p">].</span><span class="n">Length</span>
<span class="n">23</span>
</pre></div>

<p>It turns out that <code>dir</code> returns an array of objects, and PowerShell knows how to format this array (and a single item from the array) into a nice table. What can we do with it? This:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-1" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-1"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="nb">Get-ChildItem</span> <span class="n">-Filter</span> <span class="s1">&#39;*.txt&#39;</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-2" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-2"></a>&nbsp;&nbsp;<span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Length</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-3" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-3"></a>&nbsp;&nbsp;<span class="nb">Measure-Object</span> <span class="n">-Sum</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-4" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-4"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-5" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-5"></a><span class="n">Count</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">:</span> <span class="n">4</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-6" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-6"></a><span class="n">Average</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-7" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-7"></a><span class="n">Sum</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">:</span> <span class="n">26</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-8" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-8"></a><span class="n">Maximum</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-9" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-9"></a><span class="n">Minimum</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-10" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-10"></a><span class="n">StandardDeviation</span> <span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-11" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-11"></a><span class="n">Property</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-12" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-12"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-13" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-13"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="p">(</span><span class="nb">Get-ChildItem</span> <span class="n">-Filter</span> <span class="s1">&#39;*.txt&#39;</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-14" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-14"></a>&nbsp;&nbsp;<span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Length</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-15"><code data-line-number="15"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-15" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-15"></a>&nbsp;&nbsp;<span class="nb">Measure-Object</span> <span class="n">-Sum</span><span class="p">).</span><span class="n">Sum</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-16"><code data-line-number="16"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-16" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-16"></a><span class="n">26</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-17"><code data-line-number="17"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-17" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-17"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="p">(</span><span class="nb">Get-ChildItem</span> <span class="n">-Filter</span> <span class="s1">&#39;*.txt&#39;</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-18"><code data-line-number="18"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-18" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-18"></a>&nbsp;&nbsp;<span class="nb">Measure-Object</span> <span class="n">-Sum</span> <span class="n">-Property</span> <span class="n">Length</span><span class="p">).</span><span class="n">Sum</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-19"><code data-line-number="19"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-19" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-19"></a><span class="n">26</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-20"><code data-line-number="20"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-20" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-20"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="p">(</span><span class="nb">Get-ChildItem</span> <span class="n">-Recurse</span> <span class="n">-Filter</span> <span class="s1">&#39;*.txt&#39;</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-21"><code data-line-number="21"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-21" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-21"></a>&nbsp;&nbsp;<span class="nb">Measure-Object</span> <span class="n">-Sum</span> <span class="n">-Property</span> <span class="n">Length</span><span class="p">).</span><span class="n">Sum</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-22"><code data-line-number="22"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-22" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-22"></a><span class="n">30</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-23"><code data-line-number="23"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-23" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-23"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="nv">$measured</span> <span class="p">=</span> <span class="p">(</span><span class="nb">Get-ChildItem</span> <span class="n">-Recurse</span> <span class="n">-Filter</span> <span class="s1">&#39;*.txt&#39;</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-24"><code data-line-number="24"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-24" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-24"></a>&nbsp;&nbsp;<span class="nb">Measure-Object</span> <span class="n">-Sum</span> <span class="n">-Property</span> <span class="n">Length</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-25"><code data-line-number="25"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-25" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-25"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">hello</span><span class="p">&gt;</span> <span class="nv">$measured</span><span class="p">.</span><span class="n">Sum</span> <span class="p">/</span> <span class="nv">$measured</span><span class="p">.</span><span class="n">Count</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-26"><code data-line-number="26"></code></a></td><td class="code"><code><a id="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-26" name="code_b6bfcaf6e4ff4bdba3718ce7e5233412217eab04-26"></a><span class="n">6</span>
</code></td></tr></table></div>
<p>We can iterate over all file objects, get their length (using <code>ForEach-Object</code> and a lambda), and then use <code>Measure-Object</code> to compute the sum (<code>Measure-Object</code> returns an object, we need to get its <code>Sum</code> property). We can replace the <code>ForEach-Object</code> call with the <code>-Property</code> argument in <code>Measure-Object</code>. And if we want to look into subdirectories, we can easily add <code>-Recurse</code> to <code>Get-ChildItem</code>. We get actual integers we can do math on.</p>
<p>You might have noticed I used <code>Get-ChildItem</code> instead of <code>dir</code> in the previous example. <code>Get-ChildItem</code> is the full name of the command (<em>cmdlet</em>). <code>dir</code> is one of its aliases, alongside <code>gci</code> and <code>ls</code> (Windows-only to avoid shadowing <code>/bin/ls</code>). Many common commands have aliases defined for easier typing and ease of use — <code>Copy-Item</code> can be written as <code>cp</code> (for compatibility with Unix), <code>copy</code> (for compatibility with MS-DOS), and <code>ci</code>. In our examples, we could also use <code>measure</code> for <code>Measure-Object</code> and <code>foreach</code> or <code>%</code> for <code>ForEach-Object</code>. Those aliases are a nice thing to have for interactive use, but for scripts, it’s best to use the full names for readability, and to avoid depending on the environment for those aliases.</p>
<h2 id="more-filesystem-operations">More filesystem operations</h2>
<h3 id="files-per-folder">Files per folder</h3>
<p>There’s a photo collection in a <code>Photos</code> folder, grouped into folders. The objective is to see how many <code>.jpg</code> files are in each folder. Here’s the PowerShell solution:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_57265ffb293a3ee263d5186a87f7fc665c0e654a-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-1" name="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-1"></a><span class="nb">PS </span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">&gt;</span> <span class="nb">Get-ChildItem</span> <span class="n">Photos</span><span class="p">/*/*.</span><span class="n">jpg</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_57265ffb293a3ee263d5186a87f7fc665c0e654a-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-2" name="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-2"></a>&nbsp;&nbsp;<span class="nb">Group-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Directory</span><span class="p">.</span><span class="n">Name</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_57265ffb293a3ee263d5186a87f7fc665c0e654a-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-3" name="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-3"></a>&nbsp;&nbsp;<span class="nb">Sort-Object</span> <span class="n">-Property</span> <span class="n">Count</span> <span class="n">-Descending</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_57265ffb293a3ee263d5186a87f7fc665c0e654a-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-4" name="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-4"></a><span class="n">Count</span> <span class="n">Name</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">Group</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_57265ffb293a3ee263d5186a87f7fc665c0e654a-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-5" name="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-5"></a><span class="p">-----</span> <span class="p">----</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">-----</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_57265ffb293a3ee263d5186a87f7fc665c0e654a-6"><code data-line-number="6"></code></a></td><td class="code"><code><a id="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-6" name="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-6"></a>&nbsp;&nbsp;&nbsp;<span class="n">10</span> <span class="n">foo</span> <span class="n">bar</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">{</span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">Photos</span><span class="p">\</span><span class="n">foo</span> <span class="n">bar</span><span class="p">\</span><span class="n">img001</span><span class="p">.</span><span class="n">jpg</span><span class="p">,</span> <span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">Photos</span><span class="p">\</span><span class="n">foo</span> <span class="n">bar</span><span class="p">\</span><span class="n">img002</span><span class="p">.</span><span class="n">jpg</span><span class="p">,</span> <span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">Photos</span><span class="p">\</span><span class="n">foo</span> <span class="n">bar</span><span class="p">\</span><span class="n">img003</span><span class="p">.</span><span class="n">jpg</span><span class="err">…</span><span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_57265ffb293a3ee263d5186a87f7fc665c0e654a-7"><code data-line-number="7"></code></a></td><td class="code"><code><a id="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-7" name="code_57265ffb293a3ee263d5186a87f7fc665c0e654a-7"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">2</span> <span class="n">example</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">{</span><span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">Photos</span><span class="p">\</span><span class="n">example</span><span class="p">\</span><span class="n">img101</span><span class="p">.</span><span class="n">jpg</span><span class="p">,</span> <span class="n">C</span><span class="p">:\</span><span class="n">tmp</span><span class="p">\</span><span class="n">Photos</span><span class="p">\</span><span class="n">example</span><span class="p">\</span><span class="n">img201</span><span class="p">.</span><span class="n">jpg</span><span class="p">}</span>
</code></td></tr></table></div>
<p>In Unix land, <a href="https://stackoverflow.com/questions/15216370/how-to-count-number-of-files-in-each-directory">StackOverflow has a lot of solutions</a>. The top solution is <code>du -a | cut -d/ -f2 | sort | uniq -c | sort -nr</code> — a lot of tools mashed together, starting with a tool to check disk usage, and a lot of string manipulation. The second solution uses find, read, and shell globbing. The PowerShell solution is quite simple and obvious to anyone who has ever touched SQL.</p>
<p>The above example works for one level of nesting. For more levels, given <code>Photos\one\two\three.jpg</code>, use <code>Get-ChildItem -Filter '*.jpg' -Recurse Photos</code>, and:</p>
<ul>
<li>Group by <code>$_.Directory.Name</code> (same as before) to get <code>two</code></li>
<li>Group by <code>Split-Path -Parent ([System.IO.Path]::GetRelativePath(&quot;$PWD/Photos&quot;, $_.FullName))</code> to get <code>one/two</code></li>
<li>Group by <code>([System.IO.Path]::GetRelativePath(&quot;$PWD/Photos&quot;, $_.FullName)).Split([System.IO.Path]::DirectorySeparatorChar)[0]</code> to get <code>one</code></li>
</ul>
<p>(All of the above examples work for a single folder as well. The latter two examples don’t work on Windows PowerShell.)</p>
<h3 id="duplicate-finder">Duplicate finder</h3>
<p>Let’s build a simple tool to detect byte-for-byte duplicated files. <code>Get-FileHash</code> is a shell built-in. We can use <code>Group-Object</code> again, and <code>Where-Object</code> to filter only matching objects. Computing the hash of every file is quite inefficient, so we’ll group by the file length first, and then ensure the hashes match. This gives us a nice pipeline of 6 commands:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-1" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-1"></a><span class="c"># Fully spelled out</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-2" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-2"></a><span class="nb">Get-ChildItem</span> <span class="n">-Recurse</span> <span class="o">-File</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-3" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-3"></a>&nbsp;&nbsp;<span class="nb">Group-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Length</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-4" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-4"></a>&nbsp;&nbsp;<span class="nb">Where-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Count</span> <span class="o">-gt</span> <span class="n">1</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-5" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-5"></a>&nbsp;&nbsp;<span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="nb">Group </span><span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-6" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-6"></a>&nbsp;&nbsp;<span class="nb">Group-Object</span> <span class="p">{</span> <span class="p">(</span><span class="nb">Get-FileHash</span> <span class="n">-Algorithm</span> <span class="n">MD5</span> <span class="nv">$_</span><span class="p">).</span><span class="n">Hash</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-7" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-7"></a>&nbsp;&nbsp;<span class="nb">Where-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Count</span> <span class="o">-gt</span> <span class="n">1</span> <span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-8" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-8"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-9" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-9"></a><span class="c"># Using aliases</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-10" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-10"></a><span class="nb">gci </span><span class="n">-Recurse</span> <span class="o">-File</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-11" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-11"></a>&nbsp;&nbsp;<span class="nb">group </span><span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Length</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-12" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-12"></a>&nbsp;&nbsp;<span class="nb">where </span><span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Count</span> <span class="o">-gt</span> <span class="n">1</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-13" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-13"></a>&nbsp;&nbsp;<span class="k">foreach</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="nb">Group </span><span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-14" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-14"></a>&nbsp;&nbsp;<span class="nb">group </span><span class="p">{</span> <span class="p">(</span><span class="nb">Get-FileHash</span> <span class="n">-Algorithm</span> <span class="n">MD5</span> <span class="nv">$_</span><span class="p">).</span><span class="n">Hash</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-15"><code data-line-number="15"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-15" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-15"></a>&nbsp;&nbsp;<span class="nb">where </span><span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Count</span> <span class="o">-gt</span> <span class="n">1</span> <span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-16"><code data-line-number="16"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-16" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-16"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-17"><code data-line-number="17"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-17" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-17"></a><span class="c"># Using less readable aliases</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-18"><code data-line-number="18"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-18" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-18"></a><span class="nb">gci </span><span class="n">-Recurse</span> <span class="o">-File</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-19"><code data-line-number="19"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-19" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-19"></a>&nbsp;&nbsp;<span class="nb">group </span><span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Length</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-20"><code data-line-number="20"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-20" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-20"></a>&nbsp;&nbsp;<span class="p">?</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Count</span> <span class="o">-gt</span> <span class="n">1</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-21"><code data-line-number="21"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-21" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-21"></a>&nbsp;&nbsp;<span class="p">%</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="nb">Group </span><span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-22"><code data-line-number="22"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-22" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-22"></a>&nbsp;&nbsp;<span class="nb">group </span><span class="p">{</span> <span class="p">(</span><span class="nb">Get-FileHash</span> <span class="n">-Algorithm</span> <span class="n">MD5</span> <span class="nv">$_</span><span class="p">).</span><span class="n">Hash</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_23a175a4070286ab72d28ef04e77af82705e2c7c-23"><code data-line-number="23"></code></a></td><td class="code"><code><a id="code_23a175a4070286ab72d28ef04e77af82705e2c7c-23" name="code_23a175a4070286ab72d28ef04e77af82705e2c7c-23"></a>&nbsp;&nbsp;<span class="p">?</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Count</span> <span class="o">-gt</span> <span class="n">1</span> <span class="p">}</span>
</code></td></tr></table></div>
<h2 id="serious-scripting-software-bill-of-materials">Serious Scripting: Software Bill of Materials</h2>
<p>Software Bills of Materials (SBOMs) and supply chain security are all the rage these days. The boss wants to have something like that, i.e. a CSV file with a list of packages and versions, and only the direct production dependencies. Sure, there exist standards like SPDX, but the boss does not like those pesky “standards”. The backend is written in C#, and the frontend is written in Node.js. Since we care only about the production dependencies, we can look at the <code>.csproj</code> and <code>package.json</code> files. For Node packages, we’ll also try to fetch the license name from the npm API (the API is a bit more complicated for NuGet, so we’ll keep it as a <code>TODO</code> in this example).</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-1" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-1"></a><span class="nv">$ErrorActionPreference</span> <span class="p">=</span> <span class="s2">&quot;Stop&quot;</span> <span class="c"># stop execution on any error</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-2" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-2"></a><span class="nb">Set-StrictMode</span> <span class="n">-Version</span> <span class="n">3</span><span class="p">.</span><span class="n">0</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-3" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-3"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-4" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-4"></a><span class="k">function</span> <span class="nb">Get-CsprojPackages</span><span class="p">(</span><span class="no">[string]</span><span class="nv">$Path</span><span class="p">)</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-5" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-5"></a>&nbsp;&nbsp;<span class="k">return</span> <span class="nb">Select-Xml</span> <span class="n">-Path</span> <span class="nv">$Path</span> <span class="n">-XPath</span> <span class="s1">&#39;//PackageReference&#39;</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-6" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-6"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-7" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-7"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">[PSCustomObject]</span><span class="p">@{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-8" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-8"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Name</span> <span class="p">=</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Node</span><span class="p">.</span><span class="n">GetAttribute</span><span class="p">(</span><span class="s2">&quot;Include&quot;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-9" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-9"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Version</span> <span class="p">=</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Node</span><span class="p">.</span><span class="n">GetAttribute</span><span class="p">(</span><span class="s2">&quot;Version&quot;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-10" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-10"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Source</span> <span class="p">=</span> <span class="s1">&#39;nuget&#39;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-11" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-11"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">License</span> <span class="p">=</span> <span class="s1">&#39;TODO&#39;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-12" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-12"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-13" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-13"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-14" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-14"></a><span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-15"><code data-line-number="15"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-15" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-15"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-16"><code data-line-number="16"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-16" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-16"></a><span class="k">function</span> <span class="nb">Get-NodePackages</span><span class="p">(</span><span class="no">[string]</span><span class="nv">$Path</span><span class="p">)</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-17"><code data-line-number="17"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-17" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-17"></a>&nbsp;&nbsp;<span class="nv">$nameToVersion</span> <span class="p">=</span> <span class="p">(</span><span class="nb">Get-Content</span> <span class="n">-Raw</span> <span class="nv">$Path</span> <span class="p">|</span> <span class="nb">ConvertFrom-Json</span><span class="p">).</span><span class="n">dependencies</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-18"><code data-line-number="18"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-18" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-18"></a>&nbsp;&nbsp;<span class="k">return</span> <span class="nv">$nameToVersion</span><span class="p">.</span><span class="n">psobject</span><span class="p">.</span><span class="n">Properties</span> <span class="p">|</span> <span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-19"><code data-line-number="19"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-19" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-19"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="no">[PSCustomObject]</span><span class="p">@{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-20"><code data-line-number="20"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-20" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-20"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Name</span> <span class="p">=</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Name</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-21"><code data-line-number="21"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-21" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-21"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Version</span> <span class="p">=</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Value</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-22"><code data-line-number="22"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-22" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-22"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">Source</span> <span class="p">=</span> <span class="s1">&#39;node&#39;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-23"><code data-line-number="23"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-23" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-23"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">License</span> <span class="p">=</span> <span class="p">(</span><span class="nb">Get-NodeLicense</span> <span class="n">-Name</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Name</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-24"><code data-line-number="24"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-24" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-24"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-25"><code data-line-number="25"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-25" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-25"></a>&nbsp;&nbsp;<span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-26"><code data-line-number="26"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-26" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-26"></a><span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-27"><code data-line-number="27"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-27" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-27"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-28"><code data-line-number="28"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-28" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-28"></a><span class="k">function</span> <span class="nb">Get-NodeLicense</span><span class="p">(</span><span class="no">[string]</span><span class="nv">$Name</span><span class="p">)</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-29"><code data-line-number="29"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-29" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-29"></a>&nbsp;&nbsp;<span class="k">try</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-30"><code data-line-number="30"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-30" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-30"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="p">(</span><span class="nb">Invoke-RestMethod</span> <span class="n">-TimeoutSec</span> <span class="n">3</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-31"><code data-line-number="31"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-31" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-31"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;https://registry.npmjs.org/$Name&quot;</span><span class="p">).</span><span class="n">license</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-32"><code data-line-number="32"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-32" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-32"></a>&nbsp;&nbsp;<span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-33"><code data-line-number="33"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-33" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-33"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="s2">&quot;???&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-34"><code data-line-number="34"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-34" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-34"></a>&nbsp;&nbsp;<span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-35"><code data-line-number="35"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-35" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-35"></a><span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-36"><code data-line-number="36"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-36" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-36"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-37"><code data-line-number="37"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-37" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-37"></a><span class="nv">$csprojData</span> <span class="p">=</span> <span class="p">@(</span><span class="nb">Get-ChildItem</span> <span class="n">-Recurse</span> <span class="n">-Filter</span> <span class="s1">&#39;*.csproj&#39;</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-38"><code data-line-number="38"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-38" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-38"></a>&nbsp;&nbsp;<span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span> <span class="nb">Get-CsprojPackages</span> <span class="nv">$_</span><span class="p">.</span><span class="n">FullName</span> <span class="p">})</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-39"><code data-line-number="39"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-39" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-39"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-40"><code data-line-number="40"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-40" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-40"></a><span class="nv">$nodeData</span> <span class="p">=</span> <span class="p">@(</span><span class="nb">Get-ChildItem</span> <span class="n">-Recurse</span> <span class="n">-Filter</span> <span class="s1">&#39;package.json&#39;</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-41"><code data-line-number="41"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-41" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-41"></a>&nbsp;&nbsp;<span class="nb">Where-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">FullName</span> <span class="o">-notlike</span> <span class="s1">&#39;*node_modules*&#39;</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-42"><code data-line-number="42"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-42" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-42"></a>&nbsp;&nbsp;<span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span> <span class="nb">Get-NodePackages</span> <span class="nv">$_</span><span class="p">.</span><span class="n">FullName</span> <span class="p">})</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-43"><code data-line-number="43"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-43" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-43"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-44"><code data-line-number="44"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-44" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-44"></a><span class="nv">$allData</span> <span class="p">=</span> <span class="nv">$csProjData</span> <span class="p">+</span> <span class="nv">$nodeData</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-45"><code data-line-number="45"></code></a></td><td class="code"><code><a id="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-45" name="code_4fe48e7fd8d202d89c2e576d8d8b41ace059bc6a-45"></a><span class="nv">$allData</span> <span class="p">|</span> <span class="nb">ConvertTo-Csv</span> <span class="n">-NoTypeInformation</span> <span class="p">|</span> <span class="nb">Tee-Object</span> <span class="n">sbom</span><span class="p">.</span><span class="n">csv</span>
</code></td></tr></table></div>
<p>Just like every well-written shell script starts with <code>set -euo pipefail</code>, every PowerShell script should start with <a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode?view=powershell-7.4"><code>$ErrorActionPreference = &quot;Stop&quot;</code></a> so that execution is stopped as soon as something goes wrong. Note that this does <em>not</em> affect native commands, you still need to check <code>$LASTEXITCODE</code>. Another useful early command is <a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode?view=powershell-7.4"><code>Set-StrictMode -Version 3.0</code></a> to catch undefined variables.</p>
<p>For <code>.csproj</code> files, which are XML, we look for <code>PackageReference</code> elements using XPath, and then build a PSCustomObject out of a hashmap — extracting the appropriate attributes from the <code>PackageReference</code> nodes.</p>
<p>For <code>package.json</code>, we read the file, parse the JSON, and extract the properties of the <code>dependencies</code> object (it’s a map of package names to versions). To get the license, we use <code>Invoke-RestMethod</code>, which takes care of parsing JSON for us.</p>
<p>In the main body of the script, we look for the appropriate files (skipping things under <code>node_modules</code>) and call our parser functions. After retrieving all data, we concatenate the two arrays, convert to CSV, and use <code>Tee-Object</code> to output to a file and to standard output. We get this:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-1" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-1"></a><span class="s">&quot;Name&quot;</span><span class="p">,</span><span class="s">&quot;Version&quot;</span><span class="p">,</span><span class="s">&quot;Source&quot;</span><span class="p">,</span><span class="s">&quot;License&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-2" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-2"></a><span class="s">&quot;AWSSDK.S3&quot;</span><span class="p">,</span><span class="s">&quot;3.7.307.24&quot;</span><span class="p">,</span><span class="s">&quot;nuget&quot;</span><span class="p">,</span><span class="s">&quot;TODO&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-3" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-3"></a><span class="s">&quot;Microsoft.AspNetCore.SpaProxy&quot;</span><span class="p">,</span><span class="s">&quot;7.0.17&quot;</span><span class="p">,</span><span class="s">&quot;nuget&quot;</span><span class="p">,</span><span class="s">&quot;TODO&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-4" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-4"></a><span class="s">&quot;@testing-library/jest-dom&quot;</span><span class="p">,</span><span class="s">&quot;^5.17.0&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-5" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-5"></a><span class="s">&quot;@testing-library/react&quot;</span><span class="p">,</span><span class="s">&quot;^13.4.0&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-6" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-6"></a><span class="s">&quot;@testing-library/user-event&quot;</span><span class="p">,</span><span class="s">&quot;^13.5.0&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-7" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-7"></a><span class="s">&quot;@types/jest&quot;</span><span class="p">,</span><span class="s">&quot;^27.5.2&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-8" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-8"></a><span class="s">&quot;@types/node&quot;</span><span class="p">,</span><span class="s">&quot;^16.18.96&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-9" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-9"></a><span class="s">&quot;@types/react&quot;</span><span class="p">,</span><span class="s">&quot;^18.3.1&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-10" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-10"></a><span class="s">&quot;@types/react-dom&quot;</span><span class="p">,</span><span class="s">&quot;^18.3.0&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-11" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-11"></a><span class="s">&quot;react&quot;</span><span class="p">,</span><span class="s">&quot;^18.3.1&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-12" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-12"></a><span class="s">&quot;react-dom&quot;</span><span class="p">,</span><span class="s">&quot;^18.3.1&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-13" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-13"></a><span class="s">&quot;react-scripts&quot;</span><span class="p">,</span><span class="s">&quot;5.0.1&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;MIT&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-14" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-14"></a><span class="s">&quot;typescript&quot;</span><span class="p">,</span><span class="s">&quot;^4.9.5&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;Apache-2.0&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_7e45a17d671643979b2466b48fce4eb30fc60fa8-15"><code data-line-number="15"></code></a></td><td class="code"><code><a id="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-15" name="code_7e45a17d671643979b2466b48fce4eb30fc60fa8-15"></a><span class="s">&quot;web-vitals&quot;</span><span class="p">,</span><span class="s">&quot;^2.1.4&quot;</span><span class="p">,</span><span class="s">&quot;node&quot;</span><span class="p">,</span><span class="s">&quot;Apache-2.0&quot;</span>
</code></td></tr></table></div>
<p>Could it be done in a different language? Certainly, but PowerShell is really easy to integrate with CI, e.g. <a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-running-a-command-using-powershell-core">GitHub Actions</a> or <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/powershell-v2?view=azure-pipelines">Azure Pipelines</a>. On Linux, you might be tempted to use Python — and you could get something done equally simply, as long as you don’t mind using the ugly <code>urllib.request</code> library, or alternatively ensuring <code>requests</code> is installed (and then you get into the hell that is Python package management).</p>
<h2 id="using.net-classes">Using .NET classes</h2>
<p>PowerShell is built on top of .NET. This isn’t just the implementation technology — PowerShell gives access to everything the .NET standard library offers. For example, the alternate ways to group photos in multiple subdirectories we’ve explored above involve a call to a static method of the .NET <code>System.IO.Path</code> class.</p>
<p>Other .NET types are also available. Need a HashSet? Here goes:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-1" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-1"></a><span class="n">PS</span><span class="p">&gt;</span> <span class="nv">$set</span> <span class="p">=</span> <span class="nb">New-Object</span> <span class="n">System</span><span class="p">.</span><span class="n">Collections</span><span class="p">.</span><span class="n">Generic</span><span class="p">.</span><span class="n">HashSet</span><span class="no">[string]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-2" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-2"></a><span class="n">PS</span><span class="p">&gt;</span> <span class="nv">$set</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s2">&quot;hello&quot;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-3" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-3"></a><span class="n">True</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-4" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-4"></a><span class="n">PS</span><span class="p">&gt;</span> <span class="nv">$set</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s2">&quot;hello&quot;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-5" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-5"></a><span class="n">False</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-6" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-6"></a><span class="n">PS</span><span class="p">&gt;</span> <span class="nv">$set</span><span class="p">.</span><span class="n">Add</span><span class="p">(</span><span class="s2">&quot;world&quot;</span><span class="p">)</span> <span class="p">|</span> <span class="nb">Out-Null</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-7" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-7"></a><span class="n">PS</span><span class="p">&gt;</span> <span class="nv">$set</span><span class="p">.</span><span class="n">Count</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-8" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-8"></a><span class="n">2</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-9" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-9"></a><span class="n">PS</span><span class="p">&gt;</span> <span class="nv">$set</span> <span class="o">-contains</span> <span class="s2">&quot;hello&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-10" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-10"></a><span class="n">True</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-11" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-11"></a><span class="n">PS</span><span class="p">&gt;</span> <span class="nv">$set</span> <span class="o">-contains</span> <span class="s2">&quot;world&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-12" name="code_a55619a6cf811c2b6ab2c2bf17dbd8f62082087f-12"></a><span class="n">False</span>
</code></td></tr></table></div>
<p>It is also possible to load any .NET DLL into PowerShell (as long as it’s compatible with the .NET version PowerShell is built against) and use it as usual from C# (although possibly with slightly ugly syntax).</p>
<h2 id="sick-windows-tricks">Sick Windows Tricks</h2>
<p>Microsoft supposedly killed off Internet Explorer last year. Attempting to launch <code>iexplore.exe</code> will bring up Microsoft Edge. But you see, Internet Explorer is a crucial part of Windows, and has been so for over two decades. Software vendors have built software that depends on IE being there and being able to show web content. Some of them are using web views, but some of them prefer something else: COM.</p>
<p>COM, or Component Object Model, is Microsoft’s thing for interoperability between different applications and/or components. COM is basically a way for classes offered by different vendors and potentially written in different languages to talk to one another. Under the hood, COM is C++ <code>vtable</code>s plus standard reference counting and class loading/discovery mechanisms. The .NET Framework, and its successor .NET, have always included COM interoperability. The modern WinRT platform is COM on steroids.</p>
<p>Coming back to Internet Explorer, it exposes some COM classes. They were <em>not</em> removed with <code>iexplore.exe</code>. This means you can bring up a regular Internet Explorer window in just two lines of PowerShell:</p>
<div class="highlight"><pre><span></span><span class="nv">$ie</span> <span class="p">=</span> <span class="nb">New-Object</span> <span class="n">-ComObject</span> <span class="n">InternetExplorer</span><span class="p">.</span><span class="n">Application</span>
<span class="nv">$ie</span><span class="p">.</span><span class="n">Visible</span> <span class="p">=</span> <span class="nv">$true</span>
</pre></div>

<p>Why would you do that? The <code>InternetExplorer.Application</code> object lets you control the browser, e.g. you can use <code>$ie.Navigate(&quot;https://example.com/&quot;)</code> to go to a page. Why would you want to launch IE in 2024? I don’t know, I guess you can use it to laugh in the faces of the Microsoft developers who removed the user-accessible shortcuts? But there definitely exist some legacy applications that expect a COM-controllable IE.</p>
<p>We have already explored the possibility of using classes from .NET. .NET comes with a GUI framework named Windows Forms, <a href="https://learn.microsoft.com/en-us/powershell/scripting/samples/creating-a-custom-input-box?view=powershell-7.4">which can be loaded from PowerShell and used to build a GUI.</a> There is no form designer, so it requires manually defining and positioning controls, but it actually works.</p>
<p>PowerShell can also do various Windows management tasks. It can manage boot settings, BitLocker, Hyper-V, networking, storage… For example, to get the percentage of disk space remaining:</p>
<div class="highlight"><pre><span></span><span class="nv">$c</span> <span class="p">=</span> <span class="nb">Get-Volume</span> <span class="n">C</span>
<span class="s2">&quot;</span><span class="p">$((</span><span class="nv">$c</span><span class="p">.</span><span class="n">SizeRemaining</span> <span class="p">/</span> <span class="nv">$c</span><span class="p">.</span><span class="n">Size</span><span class="p">)</span> <span class="p">*</span> <span class="n">100</span><span class="p">)</span><span class="s2">%&quot;</span>
</pre></div>

<h2 id="getting-out-of-powershell-land">Getting out of PowerShell land</h2>
<p>As a shell, PowerShell can obviously launch subprocesses. Unlike something like Python, running a subprocess is as simple as running anything else. If you need to <code>git pull</code>, you just type that. Or you can make PowerShell interact with non-PowerShell commands, reading output and passing arguments:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-1" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-1"></a><span class="nv">$changes</span> <span class="p">=</span> <span class="p">(</span><span class="n">git</span> <span class="n">status</span> <span class="p">-</span><span class="n">-porcelain</span> <span class="p">-</span><span class="n">-null</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-2" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-2"></a><span class="k">if</span> <span class="p">(</span><span class="nv">$LASTEXITCODE</span> <span class="o">-eq</span> <span class="n">128</span><span class="p">)</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-3" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-3"></a>&nbsp;&nbsp;<span class="k">throw</span> <span class="s2">&quot;Not a git repository&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-4" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-4"></a><span class="p">}</span> <span class="k">elseif</span> <span class="p">(</span><span class="nv">$LASTEXITCODE</span> <span class="o">-ne</span> <span class="n">0</span><span class="p">)</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-5" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-5"></a>&nbsp;&nbsp;<span class="k">throw</span> <span class="s2">&quot;Getting changes from git failed&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-6" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-6"></a><span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-7" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-7"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-8" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-8"></a><span class="k">if</span> <span class="p">(</span><span class="nv">$null</span> <span class="o">-eq</span> <span class="nv">$changes</span><span class="p">)</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-9" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-9"></a>&nbsp;&nbsp;<span class="nb">Write-Host</span> <span class="s2">&quot;No changes found&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-10" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-10"></a><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-11" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-11"></a>&nbsp;&nbsp;<span class="nv">$untrackedFiles</span> <span class="p">=</span> <span class="p">@(</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-12" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-12"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nv">$changes</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">`0</span><span class="s2">&quot;</span><span class="p">)</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-13" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-13"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">Where-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s1">&#39;?? &#39;</span><span class="p">)</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-14" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-14"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="n">Remove</span><span class="p">(</span><span class="n">0</span><span class="p">,</span> <span class="n">3</span><span class="p">)</span> <span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-15"><code data-line-number="15"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-15" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-15"></a>&nbsp;&nbsp;<span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-16"><code data-line-number="16"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-16" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-16"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-17"><code data-line-number="17"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-17" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-17"></a>&nbsp;&nbsp;<span class="c"># Alternate spelling for regex fans:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-18"><code data-line-number="18"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-18" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-18"></a>&nbsp;&nbsp;<span class="nv">$untrackedFilesForRegexFans</span> <span class="p">=</span> <span class="p">@(</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-19"><code data-line-number="19"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-19" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-19"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nv">$changes</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">`0</span><span class="s2">&quot;</span><span class="p">)</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-20"><code data-line-number="20"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-20" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-20"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">Where-Object</span> <span class="p">{</span> <span class="nv">$_</span> <span class="o">-match</span> <span class="s1">&#39;^\?\? &#39;</span> <span class="p">}</span> <span class="p">|</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-21"><code data-line-number="21"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-21" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-21"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">ForEach</span><span class="n">-Object</span> <span class="p">{</span> <span class="nv">$_</span> <span class="o">-replace</span> <span class="s1">&#39;^\?\? &#39;</span><span class="p">,</span><span class="s1">&#39;&#39;</span> <span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-22"><code data-line-number="22"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-22" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-22"></a>&nbsp;&nbsp;<span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-23"><code data-line-number="23"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-23" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-23"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-24"><code data-line-number="24"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-24" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-24"></a>&nbsp;&nbsp;<span class="k">if</span> <span class="p">(</span><span class="nv">$untrackedFiles</span><span class="p">)</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-25"><code data-line-number="25"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-25" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-25"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">Write-Host</span> <span class="s2">&quot;Opening </span><span class="p">$(</span><span class="nv">$untrackedFiles</span><span class="p">.</span><span class="n">Length</span><span class="p">)</span><span class="s2"> untracked files in VS Code&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-26"><code data-line-number="26"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-26" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-26"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">code</span> <span class="nv">$untrackedFiles</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-27"><code data-line-number="27"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-27" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-27"></a>&nbsp;&nbsp;<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-28"><code data-line-number="28"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-28" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-28"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">Write-Host</span> <span class="s2">&quot;No untracked files&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-29"><code data-line-number="29"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-29" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-29"></a>&nbsp;&nbsp;<span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-30"><code data-line-number="30"></code></a></td><td class="code"><code><a id="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-30" name="code_2fac3f71ae9cdda9430242393e19be0d1318b0bf-30"></a><span class="p">}</span>
</code></td></tr></table></div>
<p>I chose to compute untracked files with the help of standard .NET string manipulation methods, but there’s also a regex option. On a related note, there are three content check operators: <code>-match</code> uses regex, <code>-like</code> uses <a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_wildcards?view=powershell-7.4">wildcards</a>, and <code>-contains</code> checks collection membership.</p>
<h2 id="profile-script">Profile script</h2>
<p>I use a fairly small profile script that adds some behaviours I’m used to from Unix, and to make Tab completion show a menu. Here are the most basic bits:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-1" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-1"></a><span class="nb">Set-PSReadLineOption</span> <span class="n">-HistorySearchCursorMovesToEnd</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-2" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-2"></a><span class="nb">Set-PSReadLineKeyHandler</span> <span class="n">-Key</span> <span class="n">UpArrow</span> <span class="n">-Function</span> <span class="n">HistorySearchBackward</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-3" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-3"></a><span class="nb">Set-PSReadLineKeyHandler</span> <span class="n">-Key</span> <span class="n">DownArrow</span> <span class="n">-Function</span> <span class="n">HistorySearchForward</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-4" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-4"></a><span class="nb">Set-PSReadlineKeyHandler</span> <span class="n">-Key</span> <span class="n">ctrl</span><span class="p">+</span><span class="n">d</span> <span class="n">-Function</span> <span class="n">DeleteCharOrExit</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-5" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-5"></a><span class="nb">Set-PSReadlineKeyHandler</span> <span class="n">-Key</span> <span class="n">Tab</span> <span class="n">-Function</span> <span class="n">MenuComplete</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-6" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-6"></a><span class="nb">Set-PSReadLineOption</span> <span class="n">-AddToHistoryHandler</span> <span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-7" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-7"></a>&nbsp;&nbsp;<span class="k">param</span><span class="p">(</span><span class="nv">$command</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-8" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-8"></a>&nbsp;&nbsp;<span class="c"># Commands starting with space are not remembered.</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-9" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-9"></a>&nbsp;&nbsp;<span class="k">return</span> <span class="o">-not</span> <span class="p">(</span><span class="nv">$command</span> <span class="o">-like</span> <span class="s1">&#39; *&#39;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2024/04/29/powershell-the-object-oriented-shell-you-didnt-know-you-needed/#code_774bd000262a6b91d868ee5c7d4e2939b4716853-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_774bd000262a6b91d868ee5c7d4e2939b4716853-10" name="code_774bd000262a6b91d868ee5c7d4e2939b4716853-10"></a><span class="p">}</span>
</code></td></tr></table></div>
<p>Apart from that, I use a few aliases and a pretty prompt with the help of <a href="https://ohmyposh.dev/">oh-my-posh</a>.</p>
<h2 id="the-unusual-and-sometimes-confusing-parts">The unusual and sometimes confusing parts</h2>
<p>PowerShell can be verbose. Some of its syntax is a little quirky, compared to other languages, e.g. the equality and logic operators (for example, <code>-eq</code>, <code>-le</code>, <code>-and</code>). The aliases usually help with remembering commands, but they can’t always be depended on — <code>ls</code> is defined as an alias only on Windows, and Windows PowerShell aliases <code>wget</code> and <code>curl</code> to <code>Invoke-WebRequest</code>, even though all three have completely different command line arguments and outputs (this was removed in PowerShell).</p>
<p>Moreover, the Unix/DOS aliases do not change the argument handling. <code>rm -rf foo</code> is invalid. <code>rm -r foo</code> is, since argument names can be abbreviated as long as the abbreviation is unambiguous. <code>rm -r -f foo</code> is not valid, because <code>-f</code> can be an abbreviation of <code>-Filter</code> or <code>-Force</code> (so <code>rm -r -fo foo</code>) will do. <code>rm foo bar</code> does not work, an array is needed: <code>rm foo,bar</code>.</p>
<p><code>C:\Windows\regedit.exe</code> launches the Registry editor. <code>&quot;C:\Program Files\Mozilla Firefox\firefox.exe&quot;</code> is a string. Launching something with spaces in its name requires the call operator: <code>&amp; &quot;C:\Program Files\Mozilla Firefox\firefox.exe&quot;</code>. PowerShell’s tab completion will add the <code>&amp;</code> if necessary.</p>
<p>There are two function call syntaxes. Calling a function/cmdlet uses the shell-style syntax with argument names: <code>Some-Function -Arg1 value1 -Arg2 value2</code>, and argument names can be abbreviated, and can sometimes be omitted. Calling a method requires a more traditional syntax: <code>$obj.SomeMethod(value1, value2)</code>. Names are case-insensitive in either case.</p>
<p>The escape character is the backtick. The backslash is the path separator in Windows, so making it an escape character would make everything painful on Windows. At least it makes it easy to write regex.</p>
<h2 id="the-ugliest-part">The ugliest part</h2>
<p>The ugliest and the least intuitive part of PowerShell is the handling of single-element arrays. PowerShell <em>really</em> wants to unpack them to a scalar. The command <code>(Get-ChildItem).Length</code> will produce the number of files in the current directory — <em>unless</em> there is exactly one file, in which case it will produce the single file’s size in bytes. And if there are zero items, instead of an empty array, PowerShell produces <code>$null</code>. Sometimes, things will work out in the end (since many cmdlets are happy to get either as inputs), but sometimes, PowerShell must be asked to stop this madness and return an array: <code>@(Get-ChildItem).Length</code>.</p>
<p>The previous example with <code>git status</code> leverages its <code>--null</code> argument to get zero-delimited data, so we expect either <code>$null</code> or a single string according to the rules. If we didn’t want to use <code>--null</code>, we would need to use <code>@(git status --porcelain)</code> to always get an array (but we would also need to remove quotes that <code>git</code> adds to paths that contain spaces).</p>
<h2 id="conclusion">Conclusion</h2>
<p>PowerShell is a fine interactive shell and scripting language. While it does have some warts, it is more powerful than your usual Unix shell, and its strongly-typed, object-oriented code beats <em>stringly-typed</em> <code>sh</code> spaghetti any day.</p>
]]></content:encoded><category>C#/.NET</category><category>.NET</category><category>C#</category><category>PowerShell</category><category>programming</category><category>Windows</category><category>zsh</category></item><item><title>Enabling Virtualization Support in Boot Camp with rEFInd</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2021/01/31/enabling-virtualization-support-in-boot-camp-with-refind/</link><pubDate>Sat, 30 Jan 2021 23:30:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2021/01/31/enabling-virtualization-support-in-boot-camp-with-refind/</guid><description>
You installed Windows on an Intel Mac via Boot Camp, and want to use
virtualization in it. But there’s an issue — hardware virtualization extensions
are not available. Luckily, this can be worked around easily with the help of
rEFInd, an alternate boot manager.
</description><content:encoded><![CDATA[
<p>You installed Windows on an Intel Mac via Boot Camp, and want to use
virtualization in it. But there’s an issue — hardware virtualization extensions
are not available. Luckily, this can be worked around easily with the help of
rEFInd, an alternate boot manager.</p>



<p>Many software development workflows involve virtualization. WSL, Docker for
Windows, and the Android Emulator are some examples of common
virtualization-based tools. Then there are general virtualization
tools/hypervisors, such as VMware Workstation, Hyper-V or VirtualBox. All these
tools require hardware virtualization extensions (Intel VT-x, AMD-V) or at
least are very slow without them. Virtualization extensions are not enabled by
default in the CPU, they must be enabled by something. On typical PCs, this is
often a firmware-level setting (that might be disabled by default), or it might
be unconditionally enabled by the firmware. On a Mac, however, enabling VT-x is
done by macOS, as part of the boot process. This means that Windows running in
Boot Camp will start without virtualization, unless you want to boot into macOS
first and then reboot into Windows. That setup isn’t quite ergonomic (and what
if macOS refuses to shut down, as it often does for me?).</p>
<p>Instead, we’re going to use
<a class="reference external" href="https://www.rodsbooks.com/refind/">rEFInd</a>, a boot manager for
EFI-based systems that can boot into various OSes and also handle other
parts of the boot process. But first, let’s prepare our system for this.</p>
<p class="lead"><strong>DISCLAIMER:</strong> Those steps may make your Mac fail to boot. I don’t take any
responsibility whatsoever if that happens. Prepare for the worst — make
backups, perhaps have install media ready, plan some downtime.</p>
<section id="step-1-install-windows-in-boot-camp-the-usual-way">
<h1>Step 1. Install Windows in Boot Camp the usual way</h1>
<p>The first thing you should do is install Windows 10 in Boot Camp, with
the help of the Boot Camp Assistant. The Assistant will take some time
to partition your drive and do other preparations (and show barely
informative progress bars, but <a class="reference external" href="https://chriswarrick.com/blog/2020/06/03/reinstalling-macos-what-to-try-when-all-else-fails/#an-open-letter-to-progress-bar-designers">I ranted about that Apple design “feature”
already</a>).
There are no special preparations for this, the standard process will
work. If you already have Windows installed, you can go to the next
step.</p>
</section>
<section id="step-2-ensure-the-setup-is-stable">
<h1>Step 2. Ensure the setup is stable</h1>
<p>We’ll be making changes to how the machine boots, and as such, it’s
good to have other things working correctly and in line with your
expected configuration. Make sure that:</p>
<ul class="simple">
<li><p>Both macOS and Windows boot correctly</p></li>
<li><p>You can change the OS you boot into by holding the Option key after
pressing Power (requires disabling the firmware password <a class="brackets" href="https://chriswarrick.com/blog/2021/01/31/enabling-virtualization-support-in-boot-camp-with-refind/#footnote-1" id="footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a>)</p></li>
<li><p>Disk encryption (FileVault, BitLocker) is enabled (if you want that, of
course) and fully configured (initial encryption is complete)</p></li>
<li><p>Windows setup (including Boot Camp drivers) is complete</p></li>
<li><p>The <code class="docutils literal">OSXRESERVED</code> partition that the Boot Camp Assistant created
has been deleted (that should have happened when booting into macOS for the
first time after installing Windows — complete with a slowly moving
progress bar and no other information, as is usual for this OS — but
if that didn’t happen, use Disk Utility in macOS or Recovery OS to do
that — pick your drive, click <em>Partition</em> and delete the partition,
this will grow the macOS partition)</p></li>
<li><p>System Integrity Protection is enabled (the procedure is a bit safer
that way)</p></li>
</ul>
</section>
<section id="step-3-create-a-partition-for-refind">
<h1>Step 3. Create a partition for rEFInd</h1>
<p>First, back up your data before making changes to your hard drive
layout. We’ll need to create a new partition for rEFInd to live on. This
is the safest option — you could install it to the EFI System Partition (ESP),
but macOS might want to put its own stuff there, and it’s safer not to
use it.</p>
<p>The rEFInd partition doesn’t need to be large (50 MB will be enough); it must use the HFS+ (Mac OS
Extended) file system. To create it, you have three options:</p>
<ul class="simple">
<li><p>From macOS, by shrinking the macOS partition: open Disk Utility,
choose your drive, select Partition, add a new partition, set its
size and file system (in that order!). This will take a few minutes
(10-15, or possibly more), and you won’t be able to use your Mac
during the resize.</p></li>
<li><p>From Recovery OS, by shrinking the macOS partition: same steps apply,
but it might be a bit safer than doing it from within macOS.</p></li>
<li><p>From Windows, by shrinking the Windows partition: open Disk
Management (press the Windows key and type <em>partition</em>, or open
Computer Management from Administrative Tools), right click your
Windows partition, select Shrink Volume. Enter the desired size and
click Shrink. Then, right click the unallocated space and create a
New Simple Volume. For now, choose FAT32 or exFAT; you’ll need to
reformat it as HFS+ from within macOS later (<em>Erase</em> in Disk Utility). This
will take a few seconds — and even if you include the time to reboot, it’s
faster.</p></li>
</ul>
<p>After you create the new partition and make sure it’s HFS+ (Mac OS
Extended), you can proceed with the setup. Also, if you don’t want the
partition to be visible in the Finder, run the following command (insert
the correct volume path for your system):</p>
<div class="code"><pre class="code text"><a id="rest_code_c10cbc8d5f0c4516bc298e5b3e0b15b5-1" name="rest_code_c10cbc8d5f0c4516bc298e5b3e0b15b5-1" href="https://chriswarrick.com/blog/2021/01/31/enabling-virtualization-support-in-boot-camp-with-refind/#rest_code_c10cbc8d5f0c4516bc298e5b3e0b15b5-1"></a>sudo chflags hidden /Volumes/rEFInd
</pre></div>
</section>
<section id="step-4-configure-and-install-refind">
<h1>Step 4. Configure and install rEFInd</h1>
<p>To set ue rEFInd, you’ll need to boot into macOS. <a class="reference external" href="https://www.rodsbooks.com/refind/getting.html">Download
rEFInd</a> from the
author’s website — you want the file named <em>A binary zip file</em>. Extract
this archive anywhere on your system (<code class="docutils literal">~/Downloads</code> is fine).</p>
<p>First, you’ll need to change the configuration file
<code class="docutils literal"><span class="pre">refind/refind.conf-sample</span></code>. Locate the setting named
<code class="docutils literal">enable_and_lock_vmx</code>, uncomment it (remove the <code class="docutils literal">#</code> at the start
of the line), and set its value to <code class="docutils literal">true</code>. You can also make other
configuration changes — the default <code class="docutils literal">timeout</code> of 20 seconds is
likely to be too much for your needs.</p>
<p>When your configuration file is ready, you can install rEFInd. You can
use the <code class="docutils literal"><span class="pre">refind-install</span></code> tool, or perform a manual install (check
out the <a class="reference external" href="https://www.rodsbooks.com/refind/installing.html">installation
docs</a> for more
details).</p>
<p>Before installing, you’ll need to get the device name of your rEFInd
partition. Open Disk Utility, select the partition from the left pane,
and check the <em>Device</em> field (for example, <code class="docutils literal">disk9s9</code> — it will be
<strong>different</strong> on your system, depending on your partition layout).</p>
<p>Open a Terminal, <code class="docutils literal">cd</code> into the directory where rEFInd was extracted,
and run the following command (replace <code class="docutils literal">disk9s9</code> with the device
name on your system):</p>
<div class="code"><pre class="code text"><a id="rest_code_d3a775e9f58d45929b57a28925faec5e-1" name="rest_code_d3a775e9f58d45929b57a28925faec5e-1" href="https://chriswarrick.com/blog/2021/01/31/enabling-virtualization-support-in-boot-camp-with-refind/#rest_code_d3a775e9f58d45929b57a28925faec5e-1"></a>./refind-install --ownhfs disk9s9
</pre></div>
<p>This command will produce an error if you have SIP enabled — but this
error is not important for us, the install will work without the change
that SIP prevented. <a class="brackets" href="https://chriswarrick.com/blog/2021/01/31/enabling-virtualization-support-in-boot-camp-with-refind/#footnote-2" id="footnote-reference-2" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a></p>
<p>You can now shut down your Mac and use the Option key while starting up
to choose the OS. You should see three options: Macintosh HD, EFI Boot,
and Boot Camp. The EFI Boot option is rEFInd — pick that, boot into
Windows (Microsoft EFI boot), <em>et voilà</em> — Windows can now run virtualization software.</p>
<p>There are a few more things that you can do now, depending on your OS
preferences.</p>
<ul class="simple">
<li><p>You can make rEFInd the default boot loader. Hold <em>Control</em> on the
Apple boot device selection screen and click the Power icon under the
EFI Boot drive (<a class="reference external" href="https://apple.stackexchange.com/a/73742">source for the
tip</a>).</p></li>
<li><p>You can use rEFInd to boot into macOS, although this might not work
with Big Sur according to the author (it seems to work for me, but
YMMV). You can use the standard boot method for macOS (by defaulting
to Macintosh HD, or by choosing it from the Power+Option picker) and
rEFInd exclusively for Windows (and set your timeout to a low value).</p></li>
<li><p>You can modify rEFInd’s configuration — in this scenario, the config
file is <code class="docutils literal">/Volumes/rEFInd/System/Library/CoreServices/refind.conf</code>.
You can set a custom background image, for example (<a class="reference external" href="https://www.rodsbooks.com/refind/">rEFInd’s
site</a> can help you figure out
what options are available and what you can set them to).</p></li>
</ul>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="footnote-1" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2021/01/31/enabling-virtualization-support-in-boot-camp-with-refind/#footnote-reference-1">1</a><span class="fn-bracket">]</span></span>
<p>If the firmware password is important to you, you can restore it after
the setup is done — this will mean using rEFInd to boot both Windows and
macOS, although I decided to remove the firmware password and boot
into macOS from the Power+Option boot menu.</p>
</aside>
<aside class="footnote brackets" id="footnote-2" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2021/01/31/enabling-virtualization-support-in-boot-camp-with-refind/#footnote-reference-2">2</a><span class="fn-bracket">]</span></span>
<p>The failing operation is marking the rEFInd partition bootable in the Mac
sense, using the <code class="docutils literal">bless</code> command. However, the drive is considered
bootable as an EFI-compliant boot volume (it has <code class="docutils literal">*.efi</code> files in specific
places), and this is the boot method we’re using here. SIP aside, the
<code class="docutils literal">bless</code> utility is a bit buggy, and we can use rEFInd without a blessed
partition just fine.</p>
</aside>
</aside>
</section>
]]></content:encoded><category>Apple</category><category>Boot Camp</category><category>Mac</category><category>rEFInd</category><category>Virtualization</category><category>Windows</category></item><item><title>Windows 10 November Upgrade: Windows as a (Dis-)service</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/</link><pubDate>Sun, 27 Dec 2015 11:15:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/</guid><description>





The About Windows screen.


I upgraded Windows 10 from the RTM version (build 10240, July 2015) to the
November Upgrade version (1511/build 10560). It took me a good two hours,
multiple reboots, and a BSOD.
</description><content:encoded><![CDATA[
<figure class="float-md-right" style="width: 20%">
<a class="reference external image-reference" href="https://chriswarrick.com/images/Windows10-1511.png">
<img alt="/images/Windows10-1511.thumbnail.png" src="https://chriswarrick.com/images/Windows10-1511.thumbnail.png">
</a>
<figcaption>
<p>The <em>About Windows</em> screen.</p>
</figcaption>
</figure>
<p>I upgraded Windows 10 from the RTM version (build 10240, July 2015) to the
<em>November Upgrade</em> version (1511/build 10560). It took me a good two hours,
multiple reboots, and a BSOD.</p>



<p>I upgraded to Windows 10 (from Windows 10) back in August. The upgrade
experience was okay. The main issue I had was that the error messages were a
bit uninformative. There was one error, Windows 10 claimed it <em>couldn’t check
available disk space</em>, which is a really strange way to say <em>your active
partition is a Linux one and you have a Linux bootloader set up</em>. But after
switching to the Windows bootloader, it worked fine. I only had to fight a bit
with the display drivers, but other than that, it succeeded.</p>
<p>Windows 10 claims to be <em>Windows as a Service</em>: a fast update cycle with
new Windows builds released often. Now, Linux users are accustomed to this.
Depending on your distribution, you get new versions somewhere between “every
day” (rolling release: Arch Linux, Gentoo, Debian sid), “every 6 months” (Ubuntu, Fedora), or some other time
scale.
(Three months and two weeks passed between the two “general” builds of Windows 10.)
And most of the time, the upgrades are fast and relatively painless.</p>
<p>With Windows 10, this is not the case. I booted to Windows yesterday, to set up
some new hardware and mess around with the OS. It offered the upgrade in
Windows Update. After downloading it (3 GB, according to Microsoft), it started
installing, without success. There were no error messages, and “Error” or
“Failed to install” were all I could get.</p>
<p>I found the setup log files. The error log ended with “failed to
resurrect new system”. Which didn’t make a lot of sense to me, even though I’m
quite proficient with this sort of things.</p>
<p>This can be dangerous, so I backed up my drives with Clonezilla, which is
really useful — and it ships with the Arch Linux ISO, which helped a lot.</p>
<p>So, I tried my trusty friend, the Media Creation Tool. I needed it to get the
original install to work, and it came useful now. After yet another download,
I got a USB stick with Windows 10 on it. So, I rebooted to the installer, which
told me I can’t use it and I need to start it from within Windows.</p>
<p>I rebooted back into Windows, started the installer. It took it a while to
begin the installation process (including waiting forever to get updates,
so I restarted the process and disabled updating now, and another long wait to
make sure it will work on my machine), but then it went quite fast and was at 70%
after less than 10 minutes. Sadly, that’s too good to be true. It rebooted and
started counting from zero. 90 minutes passed since I started, and the login
screen came up. Username, password, log in.</p>
<p><em>Hi, we’ve updated your PC.</em> Six more minutes, but at long last, I got my
desktop. There’s just one problem though: 1024×768 is not the screen resolution
I started the install with (oddly enough, the “Updating Windows” part was
running at 1080p for part of the process, and then went back to 768p after a
reboot). I tried installing NVIDIA drivers, and it failed — however, Windows
managed to install something and wanted a reboot (what is this, Windows 95?).
Fine, let’s reboot and get a fully functional Windows environment?</p>
<p>:( <em>Your PC ran into a problem and needs to restart.</em> A blue screen of death,
on the second boot, talking about <em>critical structure corruption in CI.dll</em>. Well, shit.
I rebooted, but I first took a little detour to the Arch Linux USB stick I
prepared before to get a sane bootloader back. I’ve done this before, and
usually requires three commands.</p>
<div class="code"><pre class="code console"><a id="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-1" name="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-1" href="https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/#rest_code_fa94c0ac7abc4e0ea04f530945b05c35-1"></a><span class="gp"># </span>mount<span class="w"> </span>/dev/sda2<span class="w"> </span>/mnt
<a id="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-2" name="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-2" href="https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/#rest_code_fa94c0ac7abc4e0ea04f530945b05c35-2"></a><span class="go">Metadata kept in Windows cache, refused to mount.</span>
<a id="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-3" name="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-3" href="https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/#rest_code_fa94c0ac7abc4e0ea04f530945b05c35-3"></a><span class="go">Failed to mount &#39;/dev/sda2&#39;: Operation not permitted</span>
<a id="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-4" name="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-4" href="https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/#rest_code_fa94c0ac7abc4e0ea04f530945b05c35-4"></a><span class="go">The NTFS partition is in an unsafe state. Please resume and shutdown</span>
<a id="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-5" name="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-5" href="https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/#rest_code_fa94c0ac7abc4e0ea04f530945b05c35-5"></a><span class="go">Windows fully (no hibernation or fast restarting), or mount the volume</span>
<a id="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-6" name="rest_code_fa94c0ac7abc4e0ea04f530945b05c35-6" href="https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/#rest_code_fa94c0ac7abc4e0ea04f530945b05c35-6"></a><span class="go">read-only with the &#39;ro&#39; mount option.</span>
</pre></div>
<p>Wait, what?! I remember my hard drive layout, and <code class="docutils literal">/dev/sda2</code> is my Linux
ext4 partition! I checked <code class="docutils literal">cfdisk</code>, and apparently I now have a <em>Hidden NTFS WinRE</em>, with Linux moved to <code class="docutils literal">/dev/sda3</code> and my extended partition moved to <code class="docutils literal">/dev/sda4</code> (what would happen if I already had four partitions?). This is typical Windows misbehaviour: <strong>not caring about other OSes that might be installed</strong>.</p>
<p>I accepted this defeat, mounted <code class="docutils literal">/dev/sda3</code>, ran <code class="docutils literal"><span class="pre">arch-chroot</span> /mnt</code> and
<code class="docutils literal"><span class="pre">syslinux-install_update</span> <span class="pre">-i</span> <span class="pre">-a</span> <span class="pre">-m</span></code> (I’m not a fan of GRUB 2, and I have a MBR
drive layout). I should test this out by rebooting into Linux.</p>
<div class="code"><pre class="code console"><a id="rest_code_501f298e96904fbcacf4959713042dfe-1" name="rest_code_501f298e96904fbcacf4959713042dfe-1" href="https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/#rest_code_501f298e96904fbcacf4959713042dfe-1"></a><span class="go">Error getting authority: Error initializing authority: Could not connect: No such file or directory (g-io-error-quark, 1)</span>
<a id="rest_code_501f298e96904fbcacf4959713042dfe-2" name="rest_code_501f298e96904fbcacf4959713042dfe-2" href="https://chriswarrick.com/blog/2015/12/27/windows-10-november-upgrade-windows-as-a-dis-service/#rest_code_501f298e96904fbcacf4959713042dfe-2"></a><span class="go">Welcome to emergency mode!</span>
</pre></div>
<p>systemd <strong>really</strong> values <code class="docutils literal">/etc/fstab</code>, and you can’t boot if any entry fails
to mount, which was the case with the Windows drive in <code class="docutils literal">/dev/sda1</code> and my
other NTFS partition (note that the error message is very unhelpful)</p>
<p>I guess I have to get into Windows first. I tried booting Windows again, this
time it worked (in glorious 1080p, which was the case on the previous boot
too).</p>
<p>So, Windows re-enabled Fast Boot (a.k.a. hibernation instead of clean shutdown)
as part of the upgrade. I hunted down the setting and restored sanity.
But when I was doing that, I noticed things were spelt (or spelled) a bit
differently than two hours ago. You see, the original Windows 7 system was US
English. After the upgrade to Windows 10, I took the opportunity to install the
British and Polish language packs (it’s a multi-user machine). Both of which
were gone. I had to fix that, too.</p>
<p>And all I got out of it were colourful window borders, which are not even in my
desired colour (Windows has a limited colour palette, even though I explicitly
set it to <a class="reference external" href="https://chriswarrick.com/brand/">#00AADD</a>). What a great way to waste a Saturday!</p>
<p>This leaves me wondering, how does this work for Windows Insiders (Microsoft’s
community beta testers)? Is the process better if Windows Update manages to
perform the install? Or is it substantially faster if you’re running on a SSD?
Perhaps the testers have dedicated machines and don’t run experimental builds
on their daily drivers. But I doubt that, because that’s a significant
investment without any financial gain from helping out a corporation. So,
virtual machines? I have no idea how they cope.</p>
<p>By the way, <code class="docutils literal">cmd.exe</code> and <code class="docutils literal">winver.exe</code> claims it’s <em>Copyright © 2016</em>. Previously, Windows copyright
notices were outdated. Now, they’re in the future.</p>
]]></content:encoded><category>Windows</category><category>rant</category><category>review</category><category>Windows</category></item><item><title>Adventures in Windows: Music Player Daemon</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2013/09/01/mpd-on-windows/</link><pubDate>Sun, 01 Sep 2013 17:15:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2013/09/01/mpd-on-windows/</guid><description>
Recently, I had to reinstall Windows.  One of the things I had to set up
was MPD, the Music Player Daemon.
This is a short guide on how to do this.
</description><content:encoded><![CDATA[
<p>Recently, I had to reinstall Windows.  One of the things I had to set up
was MPD, the <a class="reference external" href="http://musicpd.org/">Music Player Daemon</a>.</p>
<p>This is a short guide on how to do this.</p>



<section id="step-1-get-mpd-and-mpc">
<h1>Step 1: get MPD and MPC</h1>
<p>Download <a class="reference external" href="http://musicpd.org/download/win32/">MPD</a> (<code class="docutils literal"><span class="pre">mpd-x.y.z-win32.zip</span></code>, replacing <code class="docutils literal">x.y.z</code> with whatever is the current available version of this file) and <a class="reference external" href="http://musicpd.org/download/mpc/">MPC</a> (<code class="docutils literal"><span class="pre">mpc-x.y-win32-zip</span></code>).  I suggest using
<code class="docutils literal"><span class="pre">C:\mpd</span></code> as your base path.  This post assumes you actually used this
value.</p>
<p>Put everything from the <code class="docutils literal"><span class="pre">/mpd-x.y.z-win32/bin/</span></code> directory of the
<code class="docutils literal"><span class="pre">mpd-x.y.z-win32.zip</span></code> archive into your base path (<code class="docutils literal"><span class="pre">C:\mpd</span></code>).  From
the <code class="docutils literal"><span class="pre">mpc-x.y-win32.zip</span></code> archive, take <code class="docutils literal">mpc.exe</code> and
<code class="docutils literal"><span class="pre">libmpdclient-2.dll</span></code> and put them there.</p>
</section>
<section id="step-1-5-optional-but-recommended-get-gmpc">
<h1>Step 1.5 (optional, but recommended): get GMPC</h1>
<p>If you want a nice, easy, graphical interface for MPD, you should install
GMPC, the <a class="reference external" href="http://gmpclient.org/">Gnome Music Player Client</a>.  The installation is very
straightforward, so I’ll leave the details out.</p>
</section>
<section id="step-2-create-a-config-file">
<h1>Step 2: create a config file</h1>
<p>You need to create a configuration file.  A sample one is included in
<code class="docutils literal"><span class="pre">/mpd-x.y.z-win32/doc/mpdconf.sample</span></code>.  Use the <code class="docutils literal">winmm</code> output type.</p>
<p><strong>Warning:</strong> Windows Notepad will not work, because it does not understand
Unix-style line endings (<code class="docutils literal">\n</code> instead of <code class="docutils literal">\r\n</code>).</p>
<p>For <code class="docutils literal">db_file</code> and <code class="docutils literal">log_file</code>, I recommend a file in <code class="docutils literal"><span class="pre">C:\mpd\data\</span></code>.
I also recommend saving your config as <code class="docutils literal"><span class="pre">C:\mpd\data\mpd.conf</span></code> (in fact,
this is yet another assumption we make in this guide)</p>
<section id="multi-boot-corner-keep-parts-of-the-config-the-same">
<h2>Multi Boot Corner: keep parts of the config the same</h2>
<p>Because I have a dual-boot system, there are some directions for
multi-boot users.</p>
<p>Your <code class="docutils literal">music_directory</code>, <code class="docutils literal">playlist_directory</code> and <code class="docutils literal">state_file</code> <em>must</em> be the same on all the systems in your multi-boot environment.  It is recommended to use the same <code class="docutils literal">db_file</code>.</p>
<p>Your audio outputs must have the same names.</p>
</section>
</section>
<section id="step-3-test-your-configuration">
<h1>Step 3: test your configuration</h1>
<p>Open up two Command Prompt windows.  In the first one, run:</p>
<pre class="literal-block">cd C:\mpd
mpd data\mpd.conf</pre>
<p>If you see this message:</p>
<pre class="literal-block">Failed to load database: Failed to open database file &quot;&lt;name&gt;&quot;: No such file or directory</pre>
<p>then you can ignore it.  If you see another message, fix whatever it
states.  You may also use <code class="docutils literal"><span class="pre">--stdout</span></code> or <code class="docutils literal"><span class="pre">-v</span></code> to get more output
messages.</p>
<section id="multi-boot-path">
<h2>Multi Boot path</h2>
<p>If you followed the Multi Boot instructions correctly, you might be
hearing music already.  If you do not, use your other Command Prompt window and
run:</p>
<pre class="literal-block">cd C:\mpd
mpc play</pre>
<p>If it outputs the name of a song, a <code class="docutils literal">[playing]</code> line, all is well (if you
cannot hear music, unmute your audio).</p>
<p>If you do not have a mpd database yet, use the <em>Single path</em>.</p>
<p><strong>If MPD works, press Ctrl+C in the window where you ran ``mpd
datampd.conf.``</strong></p>
</section>
<section id="single-path">
<h2>Single path</h2>
<p>In the other Command Prompt window, run:</p>
<pre class="literal-block">cd C:\mpd
mpc update
mpc add &quot;FILENAME&quot;
mpc play</pre>
<p>(FILENAME should be a name of a file in your music library.  Make sure to
keep the quotes.)</p>
<p>And now you should hear music.  If you do not, try to unmute, then read
the output of <code class="docutils literal">mpc</code> and try to fix it.</p>
<p><strong>If MPD works, press Ctrl+C in the window where you ran ``mpd
datampd.conf.``</strong></p>
</section>
</section>
<section id="step-3-5-test-gmpc">
<h1>Step 3.5: test GMPC</h1>
<p>Start the Gnome Music Player Client.  Click <em>Forward</em>, <em>Connect</em>,
<em>Forward</em>, <em>Close</em> and all should be well.</p>
</section>
<section id="step-4-create-a-windows-service">
<h1>Step 4: create a Windows service</h1>
<p>In order to run the daemon in the <em>daemon</em> mode, i.e. hidden, you need to
perform some extra steps.  You cannot use Startup in the Start Menu, you
also cannot use the Run registry keys (this results in an ugly Command Prompt
window running all the time that you cannot close or hide in the tray).</p>
<p>How to solve this problem?  Just run it as a Windows service.  That is
very easy to do.</p>
<p>Open a Command Prompt window as an administrator (right-click on <em>Command
Prompt</em> in the Start Menu/on the Start Screen and choose <em>Run as administrator</em>).</p>
<p>In that window, run one very easy command (make sure to copy-paste this
exactly!):</p>
<pre class="literal-block">sc create mpd binPath= &quot;c:\mpd\mpd.exe c:\mpd\data\mpd.conf&quot;</pre>
<p>If it said:</p>
<ul class="simple">
<li><p><code class="docutils literal">[SC] CreateService SUCCESS</code>, congratulations — you are almost done!  Skip down to <em>Service configuration.</em></p></li>
<li><p><code class="docutils literal">[SC] OpenSCManager FAILED 5: Access is denied.</code>, you need an
<strong>Administrator</strong> Command Prompt.</p></li>
<li><p>something else, check your spelling or Google the error.</p></li>
</ul>
<section id="service-configuration">
<h2>Service configuration</h2>
<p>That is the easiest part of this guide.</p>
<p>Start the <em>Services</em> console.  You can get to it by typing
<code class="docutils literal">services.msc</code> into the Start Menu/Screen search (<em>Run</em> for XP or
older).</p>
<p>In the tool, find the <code class="docutils literal">mpd</code> service.  Go to the <em>Log On</em> tab, choose
<em>This account:</em> and enter your credentials there.  Hit <em>Apply</em> and go to
the <em>General</em> tab, on which you should choose the <em>Startup type</em> to be
<em>Automatic (Delayed Start)</em>.  Finish by pressing <em>Start</em>.  MPD should be
running and configured properly.  You can now hit <em>OK</em> and close the
<em>Services</em> window, along with the command prompts you have open.</p>
<p><strong>Warning:</strong> if you change your Windows password, you need to change your
password in the <em>Services</em> console as well!</p>
</section>
<section id="just-in-case-uninstalling-the-service">
<h2>Just in case: uninstalling the service</h2>
<p>First off, stop the service (in the Services console or through <code class="docutils literal">sc stop
mpd</code> in an administrator command prompt).</p>
<p>Then, run <code class="docutils literal">sc delete mpd</code>.</p>
</section>
</section>
]]></content:encoded><category>Windows</category><category>guide</category><category>mpd</category><category>Windows</category></item></channel></rss>