<?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 programming</title><link>https://chriswarrick.com/</link><atom:link href="https://chriswarrick.com/blog/tags/programming.xml" rel="self" type="application/rss+xml" /><description>A rarely updated blog, mostly about programming.</description><lastBuildDate>Wed, 06 May 2026 19:00:00 GMT</lastBuildDate><generator>https://github.com/Kwpolska/YetAnotherBlogGenerator</generator><item><title>Certified 100% Slop</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/</link><pubDate>Wed, 06 May 2026 19:00:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/</guid><description>Recently, a port of Notepad++ to macOS has been making the rounds online, mainly due to the project using the Notepad++ name without permission and in breach of the original author’s trademark. But does it deserve any attention, or is it just AI slop all the way down?
</description><content:encoded><![CDATA[<p>Recently, a port of Notepad++ to macOS has been making the rounds online, mainly due to the project using the Notepad++ name without permission and in breach of the original author’s trademark. But does it deserve any attention, or is it just AI slop all the way down?</p>



<p>After <a href="https://notepad-plus-plus.org/news/npp-trademark-infringement/">the trademark dispute</a>, the port was renamed to Nextpad++. In the contributors section of its <a href="https://github.com/nextpad-plus-plus/nextpad-plus-plus-macos">GitHub page</a>, there are three accounts. The middle one belongs to Claude, Anthropic’s LLM; specifically, this GitHub account is used by <a href="https://claude.com/product/claude-code">Claude Code</a>, an <em>agentic coding tool</em>. Or, alternatively, a vibe-coded app that will vibe-code anything you ask for, as long as you pay for the (temporarily heavily subsidized) subscription fee.</p>
<figure class="figure border rounded float-end ms-2 mb-2">
<a href="https://chriswarrick.com/images/2026-05-06-nextpad-plus-plus-macos-contributors.png" class="image-reference">
<img class="figure-img rounded mb-0" src="https://chriswarrick.com/images/2026-05-06-nextpad-plus-plus-macos-contributors.thumbnail.png" alt="The contributors of Nextpad++.">
</a>
<figcaption class="figure-caption text-center mt-1 mb-1">The contributors of Nextpad++.</figcaption>
</figure>
<p>There were 191 commits in the repository at the time of writing. Let’s check out the <a href="https://github.com/nextpad-plus-plus/nextpad-plus-plus-macos/graphs/contributors">contributors</a> page:</p>
<ul>
<li><code>@aletik</code>: 188 commits, 747k lines added, 129k lines deleted</li>
<li><code>@claude</code>: 181 commits, 734k lines addded, 129k lines deleted</li>
<li><code>@christianbaumann</code>: 2 commits, 48 lines added, 44 lines deleted</li>
</ul>
<p>This codebase was written almost entirely by Claude. Furthermore, it is easy to notice Claudisms when looking at commits not attributed to Claude, such as <a href="https://github.com/nextpad-plus-plus/nextpad-plus-plus-macos/blob/71b1a8b2fddc00af3706e69e5bb0abcc63e23a2f/src/ColumnEditorPanel.mm#L4">section headers made out of em-dashes in the initial commit (11k lines added)</a>. Another <a href="https://github.com/nextpad-plus-plus/nextpad-plus-plus-macos/commit/480f2700b7f5950c39b5748ea99145f88bd8a048">commit without a Claude attribution</a> has an extremely long commit message — again, very Claude-y, very em-dash-y<a id="fnref:1" href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#fn:1" class="footnote-ref"><sup>1</sup></a>.</p>
<p>The Claude Code attribution is the last line of the commit message. For example, here’s a random commit<a id="fnref:2" href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#fn:2" class="footnote-ref"><sup>2</sup></a>:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-1" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-1"></a>$ git show --stat 8e2b0749fc4354532d89572fb7b6ae7ec6bf8e41
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-2" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-2"></a>commit 8e2b0749fc4354532d89572fb7b6ae7ec6bf8e41
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-3" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-3"></a>Author: Andrey Letov &lt;aletik@gmail.com&gt;
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-4" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-4"></a>Date:&nbsp;&nbsp;&nbsp;Fri Apr 24 17:57:25 2026 -0400
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-5" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-5"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-6" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-6"></a>&nbsp;&nbsp;&nbsp;&nbsp;Bump host to 1.0.5
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-7" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-7"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-8" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-8"></a>&nbsp;&nbsp;&nbsp;&nbsp;CMakeLists.txt (BUNDLE_VERSION + SHORT_VERSION_STRING) and
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-9" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-9"></a>&nbsp;&nbsp;&nbsp;&nbsp;resources/Info.plist (CFBundleVersion + CFBundleShortVersionString)
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-10" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-10"></a>&nbsp;&nbsp;&nbsp;&nbsp;roll from 1.0.4 → 1.0.5. Local DMG built but not pushed / notarized
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-11" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-11"></a>&nbsp;&nbsp;&nbsp;&nbsp;yet — release ships in a couple of days.
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-12" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-12"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-13" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-13"></a>&nbsp;&nbsp;&nbsp;&nbsp;Headline addition since 1.0.4:
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-14" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-14"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Pin / unpin toggle on popped side panels (defaults to unpinned;
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-15"><code data-line-number="15"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-15" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-15"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;was forced always-on-top in v1.0.4).
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-16"><code data-line-number="16"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-16" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-16"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-17"><code data-line-number="17"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-17" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-17"></a>&nbsp;&nbsp;&nbsp;&nbsp;Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-18"><code data-line-number="18"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-18" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-18"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-19"><code data-line-number="19"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-19" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-19"></a> CMakeLists.txt&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| 4 ++--
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-20"><code data-line-number="20"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-20" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-20"></a> resources/Info.plist | 4 ++--
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-21"><code data-line-number="21"></code></a></td><td class="code"><code><a id="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-21" name="code_74f8e058cc97094fe3f0e4cfc013b6e55bfa22d2-21"></a> 2 files changed, 4 insertions(+), 4 deletions(-)
</code></td></tr></table></div>
<p>GitHub picks up those attribution lines and shows them when viewing commits (“<strong>user</strong> and <strong>claude</strong> committed”), and includes them in the commit tally on the contributors page. But how does Git know to add this attribution? Is it some magic feature where Claude secretly writes a file for Git to pick up? Nope! Claude will include that attribution when it commits the changes. But by default, Claude will <em>not</em> commit anything when it finishes implementing your request. You need to <strong>explicitly</strong> ask it to commit, or include that in your instructions files (e.g., <code>CLAUDE.md</code>).<a id="fnref:3" href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#fn:3" class="footnote-ref"><sup>3</sup></a></p>
<p>If someone were to review the changes before committing, they would most likely do it in a code editor, navigating using the Git pane, so they see the commit box right in front of them, and they could just spend a minute writing a reasonable commit message. But nah, why bother with code review, let’s just <a href="https://www.macrumors.com/2026/04/29/notepad-plus-plus-editor-comes-to-mac/">vibe our way onto MacRumors</a>.</p>
<p>So: if you see the Claude attribution appear in a commit, you can be sure the commit is unreviewed <a href="https://en.wikipedia.org/wiki/AI_slop">slop</a>. And if virtually all commits in a project have a Claude attribution, the project is <strong>certified 100% slop</strong>. Especially since <a href="https://code.claude.com/docs/en/settings#attribution-settings">you can disable the attribution in Claude configuration</a>, so the authors of those projects don’t care at all about hiding their code’s sloppiness.</p>
<div class="footnotes">
<hr>
<ol>
<li id="fn:1">
<p>LLMs learning to use em-dashes from well-typeset books has ruined good typography. I’ve been using a <a href="https://chriswarrick.com/projects/kwkeyboard/">custom keyboard layout</a> since 2012, with em-dashes and “smart” quotes, but I’ve significantly limited the use of those characters since they became the tell-tale signs of LLM writing. (This post is 100% human-written.)<a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#fnref:1" class="footnote-back-ref">&#8617;</a></p>
</li>
<li id="fn:2">
<p>I picked a relatively recent one, with a fairly short commit message, an Unicode arrow (→), an em-dash, and where the only change is updating the version numbers in two files. This is why we can’t have nice things (and RAM).<a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#fnref:2" class="footnote-back-ref">&#8617;</a></p>
</li>
<li id="fn:3">
<p>It should be noted that VS Code has recently introduced a feature where it will sneakily add a Copilot attribution, and they managed to <a href="https://github.com/microsoft/vscode/pull/310226">make it apply it to <strong>all</strong> commits, even when AI features are disabled</a> (why was that <code>all</code> value even implemented?), but <a href="https://github.com/microsoft/vscode/blob/main/extensions/git/src/repository.ts#L1489">that feature attributes Copilot</a>, not Claude, even if you’re using Claude models within VS Code.<a href="https://chriswarrick.com/blog/2026/05/06/certified-100-percent-slop/#fnref:3" class="footnote-back-ref">&#8617;</a></p>
</li>
</ol>
</div>
]]></content:encoded><category>Programming</category><category>AI</category><category>Claude</category><category>Copilot</category><category>GitHub</category><category>LLM</category><category>programming</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>Writing a Console App in C# for Fun and Profit</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/</link><pubDate>Mon, 19 Sep 2022 20:35:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/</guid><description>
I recently wrote a simple console app in C#. This post tells you more about the language, the .NET ecosystem, and why you should write your next app in it.
</description><content:encoded><![CDATA[
<p>I recently wrote a simple console app in C#. This post tells you more about the language, the .NET ecosystem, and why you should write your next app in it.</p>



<section id="what-is-the-app">
<h1>What is the app?</h1>
<p>The app is called Gitco.NET. It’s basically a rewrite of a previous Ruby script. It shows a console menu with Git branches, allowing things like filtering and toggling the display of remote branches. Simple, but quite convenient for working with Git in a terminal. I rewrote it in C# for better Windows compatibility — but the new version works on Linux and macOS equally well, can be distributed as a single executable, and is also unit-tested.</p>
</section>
<section id="what-is-c">
<h1>What is C#?</h1>
<p>C# is a modern, high-level language designed by Microsoft in 2000, heavily inspired by (and competing with) Java.</p>
<section id="the-obligatory-hello-world-program">
<h2>The obligatory hello world program</h2>
<div class="code"><pre class="code csharp"><a id="rest_code_1c3ea5cfc3ca48d7a0e8d89503eb0ef0-1" name="rest_code_1c3ea5cfc3ca48d7a0e8d89503eb0ef0-1" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_1c3ea5cfc3ca48d7a0e8d89503eb0ef0-1"></a><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&quot;Hello, world!&quot;</span><span class="p">);</span>
</pre></div>
<p>Just one line is enough. This program requires C# 10 and .NET 6, the latest versions of the language and the framework (and the implicit usings feature enabled).</p>
</section>
<section id="the-slightly-less-cool-version-of-the-hello-world-program">
<h2>The slightly less cool version of the hello world program</h2>
<div class="code"><pre class="code csharp"><a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-1" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-1" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-1"></a><span class="k">using</span><span class="w"> </span><span class="nn">System</span><span class="p">;</span>
<a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-2" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-2" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-2"></a>
<a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-3" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-3" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-3"></a><span class="k">class</span><span class="w"> </span><span class="nc">Program</span>
<a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-4" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-4" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-4"></a><span class="p">{</span>
<a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-5" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-5" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-5"></a><span class="w">  </span><span class="k">static</span><span class="w"> </span><span class="k">void</span><span class="w"> </span><span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span><span class="w"> </span><span class="n">args</span><span class="p">)</span>
<a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-6" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-6" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-6"></a><span class="w">  </span><span class="p">{</span>
<a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-7" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-7" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-7"></a><span class="w">    </span><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&quot;Hello, world!&quot;</span><span class="p">);</span>
<a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-8" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-8" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-8"></a><span class="w">  </span><span class="p">}</span>
<a id="rest_code_571d56923a92460eb9ae1a7ce09e3cea-9" name="rest_code_571d56923a92460eb9ae1a7ce09e3cea-9" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_571d56923a92460eb9ae1a7ce09e3cea-9"></a><span class="p">}</span>
</pre></div>
<p>We’ve got four lines of code (plus four lines of braces <a class="brackets" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#footnote-1" id="footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a> ). We can see the <code class="docutils literal">using</code> directive to import everything from the <code class="docutils literal">System</code> namespace, the definition of a <code class="docutils literal">class</code>, a <code class="docutils literal">Main</code> method, and a call to <code class="docutils literal">Console.WriteLine</code>.</p>
<p>We’ll talk more about C# later, highlighting some of the cooler things seen in Gitco.NET.</p>
</section>
</section>
<section id="what-is-net">
<h1>What is .NET?</h1>
<p>The term “.NET” had quite a lot of meanings over the past two decades. Microsoft accounts were once called .NET Passport, and Windows Server 2003 was almost called “Windows Server .NET 2003”. Another thing called .NET was the .NET Framework. .NET Framework is a heavily integrated component of Windows, and it’s basically what was used to run C# (and F#, and VB.NET) — it includes the virtual machine (CLR, Core Language Runtime), a lot of libraries (Framework Class Library), and a lot of Windows-specific things (such as COM, Windows Forms, WPF).</p>
<p>Microsoft’s .NET Framework is proprietary and tied to Windows. An open-source, independent re-implementation of .NET is Mono. At one point, some GNOME apps were written in Mono and Gtk#. Mono was also used in Xamarin, which can be used to write Android and iOS apps in C#.</p>
<p>But then came out .NET Core, which is Microsoft’s open-source .NET with a new runtime (CoreCLR), new set of libraries (CoreFX), and multi-platform compatibility (Linux and macOS). After a few years, .NET Core got renamed to .NET (around the time it had pretty good feature parity with the classic .NET Framework). With the new .NET, you can build console apps, web apps (using ASP.NET Core, which is a pretty cool framework), mobile apps (soon using MAUI), and desktop apps (there are a few options).</p>
</section>
<section id="gitco-net-code-tour">
<h1>Gitco.NET code tour</h1>
<p>Let’s go on a little tour of the more interesting parts of the code.</p>
<section id="snippet-1">
<h2>Snippet 1</h2>
<div class="code"><pre class="code csharp"><a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-1" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-1" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-1"></a><span class="k">public</span><span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Branch</span><span class="o">&gt;</span><span class="w"> </span><span class="n">ExtractBranchListFromGitOutput</span><span class="p">(</span><span class="kt">string</span><span class="w"> </span><span class="n">gitOutput</span><span class="p">)</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-2" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-2" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-2"></a><span class="w">  </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">gitOutput</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-3" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-3" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-3"></a><span class="w">    </span><span class="p">.</span><span class="n">TrimEnd</span><span class="p">()</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-4" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-4" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-4"></a><span class="w">    </span><span class="p">.</span><span class="n">ReplaceLineEndings</span><span class="p">(</span><span class="s">&quot;\n&quot;</span><span class="p">)</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-5" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-5" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-5"></a><span class="w">    </span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="sc">&#39;\n&#39;</span><span class="p">)</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-6" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-6" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-6"></a><span class="w">    </span><span class="p">.</span><span class="n">Select</span><span class="p">(</span><span class="n">branchLine</span><span class="w"> </span><span class="o">=&gt;</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-7" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-7" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-7"></a><span class="w">    </span><span class="p">{</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-8" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-8" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-8"></a><span class="w">      </span><span class="kt">var</span><span class="w"> </span><span class="n">isCurrent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">branchLine</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="sc">&#39;*&#39;</span><span class="p">);</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-9" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-9" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-9"></a><span class="w">      </span><span class="kt">var</span><span class="w"> </span><span class="n">branch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">branchLine</span><span class="p">[</span><span class="mi">2</span><span class="p">..];</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-10" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-10" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-10"></a><span class="w">      </span><span class="kt">var</span><span class="w"> </span><span class="n">isRemote</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">false</span><span class="p">;</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-11" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-11" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-11"></a>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-12" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-12" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-12"></a><span class="w">      </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">branch</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="n">remotePrefix</span><span class="p">))</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-13" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-13" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-13"></a><span class="w">      </span><span class="p">{</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-14" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-14" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-14"></a><span class="w">        </span><span class="n">isRemote</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">true</span><span class="p">;</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-15" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-15" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-15"></a><span class="w">        </span><span class="n">branch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kt">string</span><span class="p">.</span><span class="n">Join</span><span class="p">(</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-16" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-16" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-16"></a><span class="w">          </span><span class="sc">&#39;/&#39;</span><span class="p">,</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-17" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-17" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-17"></a><span class="w">          </span><span class="n">branch</span><span class="p">.</span><span class="n">Split</span><span class="p">(</span><span class="s">&quot; &quot;</span><span class="p">).</span><span class="n">First</span><span class="p">().</span><span class="n">Split</span><span class="p">(</span><span class="s">&quot;/&quot;</span><span class="p">).</span><span class="n">Skip</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-18" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-18" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-18"></a><span class="w">      </span><span class="p">}</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-19" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-19" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-19"></a>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-20" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-20" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-20"></a><span class="w">      </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="nf">Branch</span><span class="p">(</span><span class="n">branch</span><span class="p">,</span><span class="w"> </span><span class="n">isRemote</span><span class="p">,</span><span class="w"> </span><span class="n">isCurrent</span><span class="p">);</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-21" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-21" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-21"></a><span class="w">    </span><span class="p">})</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-22" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-22" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-22"></a><span class="w">    </span><span class="p">.</span><span class="n">OrderBy</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">)</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-23" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-23" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-23"></a><span class="w">    </span><span class="p">.</span><span class="n">ThenBy</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">IsRemote</span><span class="p">)</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-24" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-24" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-24"></a><span class="w">    </span><span class="p">.</span><span class="n">DistinctBy</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">.</span><span class="n">Name</span><span class="p">)</span>
<a id="rest_code_c308c7ebd9924d0eb94e892197c52c65-25" name="rest_code_c308c7ebd9924d0eb94e892197c52c65-25" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_c308c7ebd9924d0eb94e892197c52c65-25"></a><span class="w">    </span><span class="p">.</span><span class="n">ToList</span><span class="p">();</span>
</pre></div>
<p>This snippet defines a fairly standard pipeline that goes from <code class="docutils literal">git</code> output (a single string) to a list of parsed objects. This pipeline is a function (or a static method, to be more precise). This function uses expression-bodied members: since we can fit the entire pipeline in a single expression, we can skip the braces and the <code class="docutils literal">return</code> keyword, and instead use a more compact syntax with an arrow (<code class="docutils literal">=&gt;</code>). After some cleanups and sanitization of the string, we split the string by the <code class="docutils literal">\n</code> character, and the type of our pipeline changes from <code class="docutils literal">string</code> to <code class="docutils literal">string[]</code> (an array of strings). We then use five operations from the <code class="docutils literal">System.Linq</code> namespace. Those operations are extension methods for enumerables (<code class="docutils literal">IEnumerable&lt;T&gt;</code>) — adding <code class="docutils literal">using System.Linq;</code> at the top of your program adds those methods to any enumerables (including arrays, lists, dictionaries, sets…).</p>
<p>The first operation is a <code class="docutils literal">Select</code>. LINQ methods are inspired by SQL; the more typical name for this one would be <code class="docutils literal">map</code>. (Similarly, <code class="docutils literal">Where</code> is LINQ’s name for <code class="docutils literal">filter</code>.) The logic inside <code class="docutils literal">Select</code> is written in a multi-line anonymous function (lambda), with braces (so there’s a <code class="docutils literal">return</code>) <a class="brackets" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#footnote-2" id="footnote-reference-2" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a>. Inside that anonymous function, there are a few niceties, such as <code class="docutils literal">var</code> (type inference for variables), slicing (<code class="docutils literal"><span class="pre">[2..]</span></code>), as well as some more LINQ in string manipulations (<code class="docutils literal">.First()</code> and <code class="docutils literal">.Skip(2)</code>, which do what they say on the tin).</p>
<p>The next three operations are fairly straightforward sorting, and extracting unique values. Those use single-expression lambdas, which don’t use <code class="docutils literal">return</code>. The pipeline ends with converting <code class="docutils literal">IEnumerable&lt;Branch&gt;</code> (which appeared at the <code class="docutils literal">.Select()</code> stage) into a <code class="docutils literal">List&lt;Branch&gt;</code>.</p>
</section>
<section id="snippet-2">
<h2>Snippet 2</h2>
<div class="code"><pre class="code csharp"><a id="rest_code_390cb327a66a4edcb2286cae95421dbc-1" name="rest_code_390cb327a66a4edcb2286cae95421dbc-1" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-1"></a><span class="k">public</span><span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="n">IEnumerable</span><span class="o">&lt;</span><span class="n">BranchDisplay</span><span class="o">&gt;</span><span class="w"> </span><span class="n">FilterAndNumberBranches</span><span class="p">(</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-2" name="rest_code_390cb327a66a4edcb2286cae95421dbc-2" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-2"></a><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Branch</span><span class="o">&gt;</span><span class="w"> </span><span class="n">branches</span><span class="p">,</span><span class="w"> </span><span class="kt">string?</span><span class="w"> </span><span class="n">filter</span><span class="p">)</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-3" name="rest_code_390cb327a66a4edcb2286cae95421dbc-3" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-3"></a><span class="p">{</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-4" name="rest_code_390cb327a66a4edcb2286cae95421dbc-4" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-4"></a><span class="w">  </span><span class="kt">var</span><span class="w"> </span><span class="n">branchWidth</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">branches</span><span class="p">.</span><span class="n">Count</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-5" name="rest_code_390cb327a66a4edcb2286cae95421dbc-5" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-5"></a><span class="w">    </span><span class="p">.</span><span class="n">ToString</span><span class="p">(</span><span class="n">CultureInfo</span><span class="p">.</span><span class="n">InvariantCulture</span><span class="p">).</span><span class="n">Length</span><span class="p">;</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-6" name="rest_code_390cb327a66a4edcb2286cae95421dbc-6" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-6"></a><span class="w">  </span><span class="kt">var</span><span class="w"> </span><span class="n">numberFormatString</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">$&quot;{{0,{branchWidth}}}. &quot;</span><span class="p">;</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-7" name="rest_code_390cb327a66a4edcb2286cae95421dbc-7" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-7"></a>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-8" name="rest_code_390cb327a66a4edcb2286cae95421dbc-8" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-8"></a><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="n">branches</span><span class="p">.</span><span class="n">Select</span><span class="p">(</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-9" name="rest_code_390cb327a66a4edcb2286cae95421dbc-9" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-9"></a><span class="w">    </span><span class="p">(</span><span class="n">branch</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="p">)</span><span class="w"> </span><span class="o">=&gt;</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-10" name="rest_code_390cb327a66a4edcb2286cae95421dbc-10" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-10"></a><span class="w">      </span><span class="k">new</span><span class="w"> </span><span class="nf">BranchDisplay</span><span class="p">(</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-11" name="rest_code_390cb327a66a4edcb2286cae95421dbc-11" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-11"></a><span class="w">        </span><span class="n">Number</span><span class="p">:</span><span class="w"> </span><span class="kt">string</span><span class="p">.</span><span class="n">Format</span><span class="p">(</span><span class="n">numberFormatString</span><span class="p">,</span><span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">),</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-12" name="rest_code_390cb327a66a4edcb2286cae95421dbc-12" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-12"></a><span class="w">        </span><span class="n">BranchName</span><span class="p">:</span><span class="w"> </span><span class="n">branch</span><span class="p">.</span><span class="n">Name</span><span class="p">,</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-13" name="rest_code_390cb327a66a4edcb2286cae95421dbc-13" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-13"></a><span class="w">        </span><span class="n">IsRemote</span><span class="p">:</span><span class="w"> </span><span class="n">branch</span><span class="p">.</span><span class="n">IsRemote</span><span class="p">,</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-14" name="rest_code_390cb327a66a4edcb2286cae95421dbc-14" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-14"></a><span class="w">        </span><span class="n">IsCurrent</span><span class="p">:</span><span class="w"> </span><span class="n">branch</span><span class="p">.</span><span class="n">IsCurrent</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-15" name="rest_code_390cb327a66a4edcb2286cae95421dbc-15" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-15"></a><span class="w">      </span><span class="p">)</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-16" name="rest_code_390cb327a66a4edcb2286cae95421dbc-16" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-16"></a><span class="w">    </span><span class="p">).</span><span class="n">Where</span><span class="p">(</span><span class="n">branchDisplay</span><span class="w"> </span><span class="o">=&gt;</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-17" name="rest_code_390cb327a66a4edcb2286cae95421dbc-17" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-17"></a><span class="w">      </span><span class="n">filter</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="k">null</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">branchDisplay</span><span class="p">.</span><span class="n">BranchName</span><span class="p">.</span><span class="n">Contains</span><span class="p">(</span><span class="n">filter</span><span class="p">));</span>
<a id="rest_code_390cb327a66a4edcb2286cae95421dbc-18" name="rest_code_390cb327a66a4edcb2286cae95421dbc-18" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_390cb327a66a4edcb2286cae95421dbc-18"></a><span class="p">}</span>
</pre></div>
<p>This function adds numbers to the branch list, and then filters branches based on the user’s query. The first thing to notice is the second argument: <code class="docutils literal">string? filter</code>. C# has support for nullable types, which means the compiler warns you if you use a possibly null value somewhere it isn’t expected <a class="brackets" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#footnote-3" id="footnote-reference-3" role="doc-noteref"><span class="fn-bracket">[</span>3<span class="fn-bracket">]</span></a>. <code class="docutils literal">numberFormatString</code> uses an interpolated string, in which <code class="docutils literal">{branchWidth}</code> will be replaced with the variable defined before. In the LINQ expression, you can see two interesting things: one is a two-argument lambda for <code class="docutils literal">Select</code>, and argument names, which can be optionally passed to functions and constructors for readability or to set parameters out of order.</p>
</section>
<section id="snippet-3">
<h2>Snippet 3</h2>
<p>How much boilerplate do you need to define an immutable data class with a constructor, value equality, and a string representation?</p>
<p>Exactly zero:</p>
<div class="code"><pre class="code csharp"><a id="rest_code_f4e830a38a2b43829677894c7c4094cd-1" name="rest_code_f4e830a38a2b43829677894c7c4094cd-1" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-1"></a><span class="k">public</span><span class="w"> </span><span class="k">record</span><span class="w"> </span><span class="nf">Branch</span><span class="p">(</span>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-2" name="rest_code_f4e830a38a2b43829677894c7c4094cd-2" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-2"></a><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="n">Name</span><span class="p">,</span>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-3" name="rest_code_f4e830a38a2b43829677894c7c4094cd-3" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-3"></a><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="n">IsRemote</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">false</span><span class="p">,</span>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-4" name="rest_code_f4e830a38a2b43829677894c7c4094cd-4" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-4"></a><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="n">IsCurrent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">false</span><span class="p">);</span>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-5" name="rest_code_f4e830a38a2b43829677894c7c4094cd-5" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-5"></a>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-6" name="rest_code_f4e830a38a2b43829677894c7c4094cd-6" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-6"></a><span class="k">public</span><span class="w"> </span><span class="k">record</span><span class="w"> </span><span class="nf">BranchDisplay</span><span class="p">(</span>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-7" name="rest_code_f4e830a38a2b43829677894c7c4094cd-7" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-7"></a><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="n">Number</span><span class="p">,</span>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-8" name="rest_code_f4e830a38a2b43829677894c7c4094cd-8" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-8"></a><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="n">BranchName</span><span class="p">,</span>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-9" name="rest_code_f4e830a38a2b43829677894c7c4094cd-9" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-9"></a><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="n">IsRemote</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">false</span><span class="p">,</span>
<a id="rest_code_f4e830a38a2b43829677894c7c4094cd-10" name="rest_code_f4e830a38a2b43829677894c7c4094cd-10" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_f4e830a38a2b43829677894c7c4094cd-10"></a><span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="n">IsCurrent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">false</span><span class="p">);</span>
</pre></div>
<p>(If you want things to be mutable, you do need to write some more code. Still, all you need for encapsulated properties is <code class="docutils literal">int Foo { get; set; }</code>, which is miles better than having to write out getters and setters by hand, as you would do in Java.)</p>
</section>
</section>
<section id="dependency-management">
<h1>Dependency management</h1>
<p>Gitco.NET is a fairly simple thing, and it doesn’t need any third-party libraries, it can do its job with just the standard library.</p>
<p>However, Gitco.NET has a test suite. .NET doesn’t ship with a unit testing framework. There are three popular options, I picked xUnit (which is the most popular). I created the test project with a template, and then added a reference to the main code (under test). I ended up with the following project file (<code class="docutils literal">gitco.NET.Tests.csproj</code>):</p>
<div class="code"><pre class="code xml"><a id="rest_code_3c2ae6d4077e4f6380698e2423682114-1" name="rest_code_3c2ae6d4077e4f6380698e2423682114-1" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-1"></a><span class="nt">&lt;Project</span><span class="w"> </span><span class="na">Sdk=</span><span class="s">&quot;Microsoft.NET.Sdk&quot;</span><span class="nt">&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-2" name="rest_code_3c2ae6d4077e4f6380698e2423682114-2" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-2"></a>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-3" name="rest_code_3c2ae6d4077e4f6380698e2423682114-3" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-3"></a><span class="w">  </span><span class="nt">&lt;PropertyGroup&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-4" name="rest_code_3c2ae6d4077e4f6380698e2423682114-4" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-4"></a><span class="w">    </span><span class="nt">&lt;TargetFramework&gt;</span>net6.0<span class="nt">&lt;/TargetFramework&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-5" name="rest_code_3c2ae6d4077e4f6380698e2423682114-5" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-5"></a><span class="w">    </span><span class="nt">&lt;ImplicitUsings&gt;</span>enable<span class="nt">&lt;/ImplicitUsings&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-6" name="rest_code_3c2ae6d4077e4f6380698e2423682114-6" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-6"></a><span class="w">    </span><span class="nt">&lt;LangVersion&gt;</span>10.0<span class="nt">&lt;/LangVersion&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-7" name="rest_code_3c2ae6d4077e4f6380698e2423682114-7" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-7"></a><span class="w">    </span><span class="nt">&lt;Nullable&gt;</span>enable<span class="nt">&lt;/Nullable&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-8" name="rest_code_3c2ae6d4077e4f6380698e2423682114-8" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-8"></a><span class="w">    </span><span class="nt">&lt;IsPackable&gt;</span>false<span class="nt">&lt;/IsPackable&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-9" name="rest_code_3c2ae6d4077e4f6380698e2423682114-9" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-9"></a><span class="w">  </span><span class="nt">&lt;/PropertyGroup&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-10" name="rest_code_3c2ae6d4077e4f6380698e2423682114-10" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-10"></a>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-11" name="rest_code_3c2ae6d4077e4f6380698e2423682114-11" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-11"></a><span class="w">  </span><span class="nt">&lt;ItemGroup&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-12" name="rest_code_3c2ae6d4077e4f6380698e2423682114-12" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-12"></a><span class="w">    </span><span class="nt">&lt;PackageReference</span><span class="w"> </span><span class="na">Include=</span><span class="s">&quot;Microsoft.NET.Test.Sdk&quot;</span><span class="w"> </span><span class="na">Version=</span><span class="s">&quot;17.1.0&quot;</span><span class="w"> </span><span class="nt">/&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-13" name="rest_code_3c2ae6d4077e4f6380698e2423682114-13" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-13"></a><span class="w">    </span><span class="nt">&lt;PackageReference</span><span class="w"> </span><span class="na">Include=</span><span class="s">&quot;xunit&quot;</span><span class="w"> </span><span class="na">Version=</span><span class="s">&quot;2.4.1&quot;</span><span class="w"> </span><span class="nt">/&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-14" name="rest_code_3c2ae6d4077e4f6380698e2423682114-14" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-14"></a><span class="w">    </span><span class="nt">&lt;PackageReference</span><span class="w"> </span><span class="na">Include=</span><span class="s">&quot;xunit.runner.visualstudio&quot;</span><span class="w"> </span><span class="na">Version=</span><span class="s">&quot;2.4.3&quot;</span><span class="nt">&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-15" name="rest_code_3c2ae6d4077e4f6380698e2423682114-15" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-15"></a><span class="w">      </span><span class="nt">&lt;IncludeAssets&gt;</span>runtime;<span class="w"> </span>build;<span class="w"> </span>native;<span class="w"> </span>contentfiles;<span class="w"> </span>analyzers;<span class="w"> </span>buildtransitive<span class="nt">&lt;/IncludeAssets&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-16" name="rest_code_3c2ae6d4077e4f6380698e2423682114-16" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-16"></a><span class="w">      </span><span class="nt">&lt;PrivateAssets&gt;</span>all<span class="nt">&lt;/PrivateAssets&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-17" name="rest_code_3c2ae6d4077e4f6380698e2423682114-17" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-17"></a><span class="w">    </span><span class="nt">&lt;/PackageReference&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-18" name="rest_code_3c2ae6d4077e4f6380698e2423682114-18" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-18"></a><span class="w">    </span><span class="nt">&lt;PackageReference</span><span class="w"> </span><span class="na">Include=</span><span class="s">&quot;coverlet.collector&quot;</span><span class="w"> </span><span class="na">Version=</span><span class="s">&quot;3.1.2&quot;</span><span class="nt">&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-19" name="rest_code_3c2ae6d4077e4f6380698e2423682114-19" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-19"></a><span class="w">      </span><span class="nt">&lt;IncludeAssets&gt;</span>runtime;<span class="w"> </span>build;<span class="w"> </span>native;<span class="w"> </span>contentfiles;<span class="w"> </span>analyzers;<span class="w"> </span>buildtransitive<span class="nt">&lt;/IncludeAssets&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-20" name="rest_code_3c2ae6d4077e4f6380698e2423682114-20" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-20"></a><span class="w">      </span><span class="nt">&lt;PrivateAssets&gt;</span>all<span class="nt">&lt;/PrivateAssets&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-21" name="rest_code_3c2ae6d4077e4f6380698e2423682114-21" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-21"></a><span class="w">    </span><span class="nt">&lt;/PackageReference&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-22" name="rest_code_3c2ae6d4077e4f6380698e2423682114-22" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-22"></a><span class="w">  </span><span class="nt">&lt;/ItemGroup&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-23" name="rest_code_3c2ae6d4077e4f6380698e2423682114-23" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-23"></a>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-24" name="rest_code_3c2ae6d4077e4f6380698e2423682114-24" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-24"></a><span class="w">  </span><span class="nt">&lt;ItemGroup&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-25" name="rest_code_3c2ae6d4077e4f6380698e2423682114-25" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-25"></a><span class="w">    </span><span class="nt">&lt;ProjectReference</span><span class="w"> </span><span class="na">Include=</span><span class="s">&quot;..\gitco.NET\gitco.NET.csproj&quot;</span><span class="w"> </span><span class="nt">/&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-26" name="rest_code_3c2ae6d4077e4f6380698e2423682114-26" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-26"></a><span class="w">  </span><span class="nt">&lt;/ItemGroup&gt;</span>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-27" name="rest_code_3c2ae6d4077e4f6380698e2423682114-27" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-27"></a>
<a id="rest_code_3c2ae6d4077e4f6380698e2423682114-28" name="rest_code_3c2ae6d4077e4f6380698e2423682114-28" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#rest_code_3c2ae6d4077e4f6380698e2423682114-28"></a><span class="nt">&lt;/Project&gt;</span>
</pre></div>
<p>Yeah, it’s an XML file. But it’s pretty straightforward: there’s a <code class="docutils literal">&lt;PropertyGroup&gt;</code> with some project configuration, and two <code class="docutils literal">&lt;ItemGroup&gt;&gt;</code> tags. One of them has <code class="docutils literal">&lt;PackageReference&gt;</code> tags, which specify third-party dependencies to use. The other has a <code class="docutils literal">&lt;ProjectReference&gt;</code> to the main code, pointing at its <code class="docutils literal">.csproj</code> file. (Note that this split is arbitrary, you can have as many <code class="docutils literal">&lt;ItemGroup&gt;</code> tags as you want, you could have just one with both package and project references.)</p>
<p>How does this work? Quite simply, and transparently to the developer. Building the project will lead to packages being <em>restored</em> (fetched from NuGet, or copied from the local NuGet cache). There are no “virtual environments” to manage, there aren’t 10 competing package managers. Visual Studio will also expect both projects to be part of one solution, which is something you’d likely do anyway for convenient access to both at the same time.</p>
</section>
<section id="tooling">
<h1>Tooling</h1>
<section id="dotnet-cli">
<h2>dotnet CLI</h2>
<p>.NET has a CLI for performing typical build and project configuration tasks. You can <code class="docutils literal">dotnet build</code> a project, you can <code class="docutils literal">dotnet run</code> it, you can <code class="docutils literal">dotnet test</code> things, and you can <code class="docutils literal">dotnet publish</code>. The CLI figures out what to do, it restores the dependencies if needed, it handles the compilation of your code. If you type <code class="docutils literal">dotnet test</code> in a directory with your solution file (<code class="docutils literal">.sln</code>), it will restore dependencies, build the code, and then find tests and run them.</p>
</section>
<section id="ide">
<h2>IDE</h2>
<p>What IDE should you use? There are a few options:</p>
<ul class="simple">
<li><p><strong>Visual Studio Code.</strong> The quite advanced text editor supports pretty much any language. C# support works okay, with all the IDE features available, but in my experience, it can sometimes get confused (requiring a restart of the IDE). You will probably need to spend some more time with the <code class="docutils literal">dotnet</code> CLI than you would with the other options.</p></li>
<li><p><strong>Visual Studio for Windows.</strong> The IDE with the purple icon is an option, although VS can feel arcane to people used to other IDEs/editors, and the Vim bindings are quite bad (especially if you select things with a mouse sometimes). It’s free for personal and very-small-business use, but for anything even slightly serious, you’ll need paid licenses.</p></li>
<li><p><strong>Visual Studio for Mac.</strong> A completely separate product, works reasonably well, same pricing as with the Windows version.</p></li>
<li><p><strong>Visual Studio for Windows + ReSharper Ultimate.</strong> Adding this (paid) extension makes VS much smarter, although it can also affect performance negatively.</p></li>
<li><p><strong>JetBrains Rider.</strong> This is an IDE based on the IntelliJ platform, with all the magic seen in ReSharper (as well as other JetBrains IDEs), but none of the performance issues and Visual Studio being Visual Studio (although if you do prefer VS behaviors and keyboard shortcuts, you can configure those as well). This is probably your best bet if you’re willing to invest some money (or your employer is).</p></li>
</ul>
</section>
</section>
<section id="why-should-i-pick-it-over-x">
<h1>Why should I pick it over X?</h1>
<p>Well, it depends. If this post has piqued your interest, perhaps install the SDK and write some small things to get a feel for the language and to see if it’s for you. (And note this post didn’t cover the Web stuff.)</p>
<p>But here are a few things of note:</p>
<section id="python">
<h2>Python</h2>
<ul class="simple">
<li><p>C# is statically typed. Modern Python’s static typing (via things like mypy) is quite cool, but not all libraries and ecosystems have adopted it. Statically typed languages are safer, and allow IDEs to be smarter.</p></li>
<li><p>C# has a better approach to functional programming. Python has ugly and single-expression lambdas (with a pointless <code class="docutils literal">lambda</code> keyword), C# has inline functions that can contain multiple statements.</p></li>
<li><p>C# has much better package management.</p></li>
<li><p>C# is trivial to compile to a single-file executable.</p></li>
<li><p>C# is much faster than Python.</p></li>
<li><p>~Nobody does machine learning and data science in C#, which is a plus in my book.</p></li>
</ul>
</section>
<section id="java">
<h2>Java</h2>
<ul class="simple">
<li><p>C# has a lot more developer-quality-of-life features and less boilerplate. For example, Lists and Dictionaries can be accessed using brackets, and properties are accessible via dot notation instead of having to explicitly call getter and setter methods.</p></li>
<li><p>C#’s generic are more flexible, as they aren’t erased on compilation.</p></li>
<li><p>C# has null safety. It also has the safe navigation <code class="docutils literal"><span class="pre">?.</span></code> operator, and the null coalescing <code class="docutils literal"><span class="pre">??</span></code> operator, both of which make working with nullable values easier.</p></li>
<li><p>C# has easy concurrency via <code class="docutils literal">async</code> and <code class="docutils literal">await</code>.</p></li>
<li><p>Web stuff: Spring is painful, Spring Boot doesn’t make it much better. ASP.NET Core is much nicer.</p></li>
</ul>
<p><em>Additional reading:</em> Wikipedia has a very nice and detailed <a class="reference external" href="https://en.wikipedia.org/wiki/Comparison_of_C_Sharp_and_Java">Comparison of C# and Java</a>.</p>
</section>
<section id="also">
<h2>Also…</h2>
<ul class="simple">
<li><p>C# is a high-level language with automated memory management, which is very convenient in many use-cases.</p></li>
<li><p>C# has exceptions.</p></li>
<li><p>There are quite a lot of jobs for C# developers, although not necessarily in Silicon Valley.</p></li>
</ul>
</section>
<section id="but-on-the-other-hand">
<h2>But on the other hand…</h2>
<ul class="simple">
<li><p>C# can still sometimes feel a bit Windows-oriented.</p></li>
<li><p>C# jobs tend to be enterprisey.</p></li>
<li><p>Python is a great language to learn as a beginner. It’s also great for one-off things, interactive work, and scripting.</p></li>
<li><p>The non-Windows desktop GUI story isn’t too great, although it is getting better with MAUI (which supports macOS).</p></li>
<li><p>If you’re targeting mobile, I would probably focus on the native APIs and languages for the best user experience (Swift and Cocoa Touch for iOS; Kotlin and the Android Platform APIs for Android). That said, MAUI might be worth a go as well.</p></li>
<li><p>If you’re doing very low-level stuff, C# probably won’t cut it.</p></li>
<li><p>If you want real functional programming, go with F#. You might also prefer Scala or Haskell or such.</p></li>
<li><p>And if you’re making web front-end stuff, TypeScript (or plain JavaScript) is still your best bet. C# has Blazor, but I’d prefer for web apps not to embed all of .NET via WebAssembly.</p></li>
</ul>
<p>But for console apps, Windows desktop, and web back-end services? <strong>Do give C# a try,</strong> it might just win you over. It is a pretty good language, but one that was held back by the Windows association for a long time. But now it’s part of a modern, multi-platform, developer-friendly ecosystem.</p>
</section>
</section>
<section id="footnotes">
<h1>Footnotes</h1>
<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/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#footnote-reference-1">1</a><span class="fn-bracket">]</span></span>
<p>The code samples in this post are using the usual Microsoft code style with braces on separate lines, the usual Microsoft naming convention (PascalCase for ~everything, camelCase for local variable names), and 2-space indentation, which isn’t the usual Microsoft style.</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/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#footnote-reference-2">2</a><span class="fn-bracket">]</span></span>
<p>This could be moved to a separate static method. If that method was <code class="docutils literal">private static Branch ParseLineAsBranch(string branchLine)</code>, then the expression could be <code class="docutils literal">.Select(ParseLineAsBranch)</code>.</p>
</aside>
<aside class="footnote brackets" id="footnote-3" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2022/09/19/writing-a-console-app-in-c-sharp-for-fun-and-profit/#footnote-reference-3">3</a><span class="fn-bracket">]</span></span>
<p>There’s some inconsistency and mixing when working with nullables: nullable objects (such as <code class="docutils literal">string?</code>) are accessible directly, whereas nullable value types (such as <code class="docutils literal">int?</code>) need to be accessed with <code class="docutils literal">.Value</code>, due to historical reasons and implementation details.</p>
</aside>
</aside>
</section>
]]></content:encoded><category>C#/.NET</category><category>.NET</category><category>C#</category><category>programming</category><category>web development</category></item><item><title>Apple broke the display menu, so I wrote my own</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2017/06/24/apple-broke-the-display-menu-so-i-wrote-my-own/</link><pubDate>Sat, 24 Jun 2017 19:02:47 GMT</pubDate><guid>https://chriswarrick.com/blog/2017/06/24/apple-broke-the-display-menu-so-i-wrote-my-own/</guid><description>
A short time ago, the macOS display menu stopped working for me. It no longer had options to change mirroring settings, only supporting AirPlay. So I wrote my own, also solving some other issues.
</description><content:encoded><![CDATA[
<p>A short time ago, the macOS display menu stopped working for me. It no longer had options to change mirroring settings, only supporting AirPlay. So I wrote my own, also solving some other issues.</p>



<p>The first thing to consider is: what features do I really want? I’m using a MacBook Pro with an external display. I sometimes need to switch to only one display <a class="brackets" href="https://chriswarrick.com/blog/2017/06/24/apple-broke-the-display-menu-so-i-wrote-my-own/#footnote-1" id="footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a> — mirroring is useful here. Although I can afford to put the dock at the bottom in dual-screen mode, it works better on the right if I’m tight on space. <a class="brackets" href="https://chriswarrick.com/blog/2017/06/24/apple-broke-the-display-menu-so-i-wrote-my-own/#footnote-2" id="footnote-reference-2" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a> Also when working on the MacBook screen, I can switch to a higher resolution. So, I need:</p>
<ul class="simple">
<li><p>mirroring</p></li>
<li><p>screen resolution</p></li>
<li><p>dock position and icon size</p></li>
</ul>
<p>But I don’t want to pick all those three independently — a preset, like <em>dual screen</em> or <em>MacBook only, 1440×900</em> works better.</p>
<p>So, I wrote <strong>Display Menu</strong>. It’s a simple menu bar extra — I haven’t yet built any friendly configuration GUI for it, so all you get for now is a JSON file. It’s roughly 600 lines of Swift. The app doesn’t do much, other than displaying an icon in the menu bar, and setting display preferences when asked to. But hey, it works for me.</p>
<p>Also, I must admit that Swift is a pretty nifty thing. Although the function to read JSON files needs to do a ton of type casting, and some of the low-level stuff looks as ugly as in C, but other than that, Swift is a modern, friendly language.</p>
<p>Display Menu is open-source, licensed under the 3-clause BSD license. <a class="reference external" href="https://github.com/Kwpolska/DisplayMenu">Fork it on GitHub</a> or <a class="reference external" href="https://github.com/Kwpolska/DisplayMenu/releases">download binary releases</a>.</p>
<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/2017/06/24/apple-broke-the-display-menu-so-i-wrote-my-own/#footnote-reference-1">1</a><span class="fn-bracket">]</span></span>
<p>Inkscape/Xorg don’t work well with multiple displays. I can also mirror and turn off the external screen or use it with some other device.</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/2017/06/24/apple-broke-the-display-menu-so-i-wrote-my-own/#footnote-reference-2">2</a><span class="fn-bracket">]</span></span>
<p>also, in dual-screen mode, I can have the dock on one screen only, and the dock must live on the edge of the “extended” display — so either on the left side of the MacBook screen, or on the right side of the external display.</p>
</aside>
</aside>
]]></content:encoded><category>Programming</category><category>app</category><category>Apple</category><category>display</category><category>programming</category><category>projects</category><category>Swift</category></item><item><title>5 months with a MacBook Pro: beautiful machine that just works</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2016/12/28/5-months-with-a-macbook-pro-beautiful-machine-that-just-works/</link><pubDate>Tue, 27 Dec 2016 15:15:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2016/12/28/5-months-with-a-macbook-pro-beautiful-machine-that-just-works/</guid><description>
Five months ago, I decided to make the switch from my trusty old desktop
computer, running Arch Linux, to a MacBook Pro. I picked the 2015 13&amp;quot; base
model with an upgraded hard drive. The device is beautiful, and just works™,
which is pretty important to me.
</description><content:encoded><![CDATA[
<p>Five months ago, I decided to make the switch from my trusty old desktop
computer, running Arch Linux, to a MacBook Pro. I picked the 2015 13&quot; base
model with an upgraded hard drive. The device is beautiful, and just works™,
which is pretty important to me.</p>



<section id="mac-as-a-unix-machine-nothing-beats-a-terminal">
<h1>Mac as a UNIX® machine: nothing beats a terminal</h1>
<p>What are the first things you should set up on a Mac, as a programmer?
<a class="reference external" href="http://brew.sh/">Homebrew</a> (and Homebrew Cask). That’s a package manager
that can install all software necessary to create an useful command-line
environment, including Python, ffmpeg and sox for media needs, git, GCC (clang
is wonderful, but I need plain old GCC sometimes), zsh, and a handful of other
programs. And, of course, Vim.</p>
<p>And what Homebrew Cask can do for you? Install many GUI programs, without
needing to mess with <code class="docutils literal">.dmg</code> installers or stuff.  Including web browsers, music
players, or iTerm2.</p>
<p>Did I mention UNIX®, with the registered trademark sign (belongs to The Open
Group; used for informational purposes only)? macOS has a fancy certificate to
prove it’s compliant with the relevant specifications. It runs the FreeBSD
userland, which is what you expect from a typical *nix system. (Linux converts
might get slightly annoyed at behavioral differences, for example <code class="docutils literal">rm
directory <span class="pre">-rf</span></code> will work on Linux with GNU coreutils, but won’t on
macOS/FreeBSD)</p>
</section>
<section id="the-gui-beautiful-fluid-friendly">
<h1>The GUI: beautiful, fluid, friendly</h1>
<p>macOS is famous for its user interface. The macOS GUI is well thought out, even
though there are some idiosyncrasies a long-time Linux/Windows user might
consider weird. For example, sorting folders before files is something natural
for Windows, but on macOS, it’s a brand new option — added in macOS Sierra,
which came out in September 2016.</p>
<p>That aside, the macOS user interface makes one coherent product. You can expect
consistent behavior between apps, and that often extends to third-party
software. Apple has a document, called <a class="reference external" href="https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html">Human Interface Guidelines</a>, which
describes how a macOS app should behave. While there are some documents like
this one for Windows, you can see many apps ignoring what it says — including
eg. built-in software, which cannot even decide on which font to use (bitmap MS
Sans Serif vs vectorized Microsoft Sans Serif vs Segoe UI — what is going on?!)</p>
<p>While the interface is friendly and coherent, it can get a little worse
when <em>foreign</em> apps are involved — for example, Qt or wxWidgets apps can
sometimes differ in behavior, but that’s not noticeable. X11 apps are another
story, but most of their developers are not aware that someone is running them
on macOS. (Excluding the Inkscape developers, which have a Mac “app” that
basically runs it in X11 and they do not even care…)</p>
</section>
<section id="programming-old-habits-die-hard">
<h1>Programming: old habits die hard</h1>
<p>Did I mention Vim? Well, I’m still using Vim and a terminal emulator to get a
lot of coding work done. Why? Because they are still the best ways to be
productive. I tried many <em>gooey</em> solutions for coding, from the heavyweights
(PyCharm, Visual Studio) to the laughable Atom editor (famous for being slow,
and effectively a web browser) — and none of them was able to replace Vim and a
Terminal. They are far too addictive.</p>
<p>That said, I sometimes use GitHub for Desktop, or other helper tools.
Sometimes, they work well — key word here is <em>sometimes</em>. Unlike Vim (or
NeoVim, or a GUI: MacVim/VimR), which boosts my productivity by a lot.</p>
<p>Honorable mention goes to Automator and AppleScript. They are a superb
solution for automating common tasks in the GUI, something other OSes do not
provide. With Automator, everyone can create a workflow to perform repetitive
tasks faster. With AppleScript, you can get even more stuff done.</p>
</section>
<section id="the-trackpad-addictive">
<h1>The trackpad: addictive</h1>
<p>Apple is famous for their trackpads. Their newest generation of these devices
does not really move when you click it, it uses the Taptic Engine and [insert
smart-sounding words here] to <em>simulate</em> a click. It also supports Force Touch,
for pressing down harder on something (eg. a word to reveal dictionary
definitions), and haptic feedback for certain operations <em>(in Soviet Russia,
trackpad clicks you!)</em></p>
<p>Those trackpads also provide intuitive gestures. Working with
full-screen windows or multiple desktops? Just swipe left/right to switch
between them. Need to see all your windows? Swipe up with three fingers. Smooth
zoom, scrolling and rotating can also be done with just the trackpad.</p>
<p>And recently I had to do some stuff on someone else’s Windows notebook. That
notebook features a touchpad that does not <em>click</em> — it has two buttons on the
bottom, and tap to click is enabled. I had to drag and drop some files between
two windows. I tried doing it the way I got used on the MBP trackpad, which is
basically the way you’d do it with a mouse: hover cursor above file, click the
trackpad, move mouse to other windows, and release. That doesn’t work on those
non-clicky touchpads. A software developer failing at drag-and-drop must be a
funny thing to see. That’s just how addictive the trackpad is. (Of course,
Windows notebooks with clicky trackpads exist, but are not as popular as the
tappy ones.)</p>
</section>
<section id="walled-garden-how-can-you-not-love-our-products">
<h1>Walled garden: how can you NOT love our products?</h1>
<p>Of course, there are some issues with living in an Apple walled garden. The
main issue is: if you want to use something that is not an Apple product, good
luck with that. Sure, you can use an Android phone, but you won’t get
some of the nice Handoff features, and if you want to transfer files, have fun
using a forgotten barely-working app from 2012. That phone also won’t be able
to access your iCloud stuff, so put your data somewhere else.</p>
<p>Do you want to use an external hard drive, or a USB stick? With other operating
systems? Well, you might have an issue with the file system. You can choose
between ExFAT, which is not popular but kinda does the job; FAT32, which has a
4GB file size limit (virtual disk images are often larger than that), or NTFS,
but for that you will need to pay a third-party company — and trust them not to
do anything nefarious. Or use experimental built-in support, or an open-source
project, both of which aren’t something one would normally trust with important
data.</p>
<p>Speaking of external hard drives, here’s a hint: if you want to use a drive for
Time Machine (a wonderful, foolproof, one-click backup solution), and you want
it encrypted, make sure it uses GPT and not MBR. I had to reformat my drive
twice, and that’s not well documented (you need to click the help button,
then go through 3 pages to find a mention of this).</p>
<p>Do you want to play some games? Well, there is basically no support for
gamepads, only some community beta drivers for the official PS3/Xbox 360 pads.
Apple does not care.</p>
<p>And then we get to mouse issues. You see, even though the trackpad is awesome,
I also want to use a regular mouse. So I started with my old PC mouse, as a
temporary solution. The mouse was a Logitech M560, which uses the wireless
Unifying USB connector. It turns out the middle mouse button is supposed to be
a Windows button on one click and left mouse button on another, but Linux
drivers seem to change that behavior. To fix that, I’d need drivers for macOS.
Logitech believes this mouse is not worthy of a Mac, and so the mouse is not
detected by their driver suite. I got rid of that mouse and replaced it with a
Microsoft Sculpt Comfort Mouse. The mouse has a real middle button, which is
activated by clicking the scroll wheel, and a Windows button on the side
(generally useless on macOS).</p>
<p>Sadly, macOS insists on scrolling in a weird accelerated way, where the number
of pixels scrolled grows over time — which means scrolling by one step means
scrolling by 5 pixels, but the longer you scroll, the larger the scroll
becomes.</p>
</section>
<section id="the-future-im-worried">
<h1>The future: I’m worried</h1>
<p>I made the decision to buy the MacBook Pro in the middle of rumor season, after
WWDC which left a lot of people disappointed. I decided that, if all the rumors
about removed ports and touchy-feely screens were true, I would not want that
device on my desk.</p>
<p><strong>And boy did Apple deliver!</strong> The new MacBook Pro has only USB-C ports (and a
headphone jack!), a gimmicky Touch Bar that only helps with emoji (the rest can
be done with standard keyboard shortcuts, or on-screen toolbars — I thought
that was a Pro machine, not a toy?), and a fingerprint reader (which I don’t
care about). And then there’s the cheaper model, with two USB-C ports and no
touch interfaces. My MBP, mainly a desktop replacement, is on AC power all the
time, and runs an external HDMI display. With the cheaper model (worse CPU than
2015; same price as 2015 with the same 256GB drive), I would have zero ports
for any other external devices. And I often have some thing plugged in, in
which case the only unoccupied ports are the Thunderbolt ports (which I don’t
have any devices for).</p>
<p>So, I hope this 2015 model will live on for years, and hopefully when it fails,
Apple will have a more sensible machine out there. For now, I’ll keep my
<em>MacBook Pro (Retina, 13-inch, Early 2015)</em> and will be pretty happy with it.</p>
<p>With just only one exception: two kernel panics in nearly 5 months. A bit
unstable, eh?</p>
</section>
]]></content:encoded><category>Apple</category><category>Apple</category><category>MacBook Pro</category><category>programming</category><category>review</category></item><item><title>Code writing code: Python and Vim as development aids</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/</link><pubDate>Fri, 27 May 2016 08:46:35 GMT</pubDate><guid>https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/</guid><description>
Recently I was working on some C# and Java code. And along the way, I used
Python and Vim to (re)write my code. A small Python script and a 6-keystroke
Vim macro did it faster and better than a human would.
Every programmer should learn a good scripting language and use a programmable
editor like Vim. Why? Here are two examples, after the break.
</description><content:encoded><![CDATA[
<p>Recently I was working on some C# and Java code. And along the way, I used
Python and Vim to (re)write my code. A small Python script and a 6-keystroke
Vim macro did it faster and better than a human would.</p>
<p>Every programmer should learn a good scripting language and use a programmable
editor like Vim. Why? Here are two examples, after the break.</p>



<section id="episode-i-inotifypropertychanged-or-python-writing-c">
<h1>Episode I: <code class="docutils literal">INotifyPropertyChanged</code>, or Python writing C#</h1>
<p>I was building a private C# weekend project (that turned into a weeklong
project) — and by the way, WPF and C# are quite pleasant (Windows Forms is a
trainwreck, though). One of the things I used in that project was a DataGrid
bound to a list of custom objects (a DataGrid is a table, basically). And in
order to use it, you need to use the <code class="docutils literal">INotifyPropertyChanged</code> interface <a class="reference external" href="https://msdn.microsoft.com/en-us/library/ms229614(v=vs.100).aspx">(MSDN)</a>.
It involves doing something like this:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-1" name="rest_code_4c40930703c34f739bfa440b60bb2d79-1"></a><span class="w"> </span><span class="k">private</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="n">name_</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">get</span><span class="p">;</span><span class="w"> </span><span class="k">set</span><span class="p">;</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// can also be a field</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-2" name="rest_code_4c40930703c34f739bfa440b60bb2d79-2"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-3" name="rest_code_4c40930703c34f739bfa440b60bb2d79-3"></a><span class="w"> </span><span class="na">[JsonProperty]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-4" name="rest_code_4c40930703c34f739bfa440b60bb2d79-4"></a><span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-5" name="rest_code_4c40930703c34f739bfa440b60bb2d79-5"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="k">get</span><span class="w"> </span><span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-6" name="rest_code_4c40930703c34f739bfa440b60bb2d79-6"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="k">return</span><span class="w"> </span><span class="n">name_</span><span class="p">;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-7" name="rest_code_4c40930703c34f739bfa440b60bb2d79-7"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-8" name="rest_code_4c40930703c34f739bfa440b60bb2d79-8"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="k">set</span><span class="w"> </span><span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-9" name="rest_code_4c40930703c34f739bfa440b60bb2d79-9"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">value</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">name_</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-10" name="rest_code_4c40930703c34f739bfa440b60bb2d79-10"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="n">name_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">value</span><span class="p">;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-11" name="rest_code_4c40930703c34f739bfa440b60bb2d79-11"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="n">NotifyPropertyChanged</span><span class="p">(</span><span class="s">&quot;name&quot;</span><span class="p">);</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-12" name="rest_code_4c40930703c34f739bfa440b60bb2d79-12"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-13" name="rest_code_4c40930703c34f739bfa440b60bb2d79-13"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="p">}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4c40930703c34f739bfa440b60bb2d79-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="rest_code_4c40930703c34f739bfa440b60bb2d79-14" name="rest_code_4c40930703c34f739bfa440b60bb2d79-14"></a><span class="w"> </span><span class="p">}</span>
</code></td></tr></table></div><p>That’s 12 lines of code (excluding <code class="docutils literal">[JsonProperty]</code> which comes from the
Json.NET library) for that pattern. Oh: and I need to do that for <strong>every</strong>
field/property of my class, because otherwise any changes to them would not be
reflected in the tables (and maybe one or two fields were <em>not</em> in the table).</p>
<p>Doing that by hand is really not feasible: you need to copy-paste this
large block 14 times and take care of 5 instances of the name (3 with
underscores, 2 without), 2 instances of the type, and the <code class="docutils literal">[JsonProperty]</code>
attribute (which does not appear on all properties).</p>
<p>So, I used one of those intelligent computer things to do it for me. I wrote a
really simple <a class="reference external" href="https://www.python.org/">Python</a> script and ran it. And I ended up with all 14 fields built
for me.</p>
<p><a class="reference external" href="link://listing/listings/code-writing-code/write_properties.py">code-writing-code/write_properties.py</a>  <a class="reference external" href="link://listing_source/listings/code-writing-code/write_properties.py">(Source)</a></p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-1" name="rest_code_5defa6b60bf345e8ac072f136362524b-1"></a><span class="ch">#!/usr/bin/env python3</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-2" name="rest_code_5defa6b60bf345e8ac072f136362524b-2"></a><span class="n">TEMPLATE</span> <span class="o">=</span> <span class="s2">&quot;&quot;&quot;</span><span class="se">\</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-3" name="rest_code_5defa6b60bf345e8ac072f136362524b-3"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="si">%s</span><span class="s2">public </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2"> {</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-4" name="rest_code_5defa6b60bf345e8ac072f136362524b-4"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get {</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-5" name="rest_code_5defa6b60bf345e8ac072f136362524b-5"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return </span><span class="si">%s</span><span class="s2">_;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-6" name="rest_code_5defa6b60bf345e8ac072f136362524b-6"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-7" name="rest_code_5defa6b60bf345e8ac072f136362524b-7"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set {</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-8" name="rest_code_5defa6b60bf345e8ac072f136362524b-8"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (value != </span><span class="si">%s</span><span class="s2">_) {</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-9" name="rest_code_5defa6b60bf345e8ac072f136362524b-9"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="si">%s</span><span class="s2">_ = value;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-10" name="rest_code_5defa6b60bf345e8ac072f136362524b-10"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NotifyPropertyChanged(&quot;</span><span class="si">%s</span><span class="s2">&quot;);</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-11" name="rest_code_5defa6b60bf345e8ac072f136362524b-11"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-12" name="rest_code_5defa6b60bf345e8ac072f136362524b-12"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-13" name="rest_code_5defa6b60bf345e8ac072f136362524b-13"></a><span class="s2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-14" name="rest_code_5defa6b60bf345e8ac072f136362524b-14"></a><span class="s2">&quot;&quot;&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-15"><code data-line-number="15"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-15" name="rest_code_5defa6b60bf345e8ac072f136362524b-15"></a><span class="n">JSONPROPERTY_TEMPLATE</span> <span class="o">=</span> <span class="s1">&#39;[JsonProperty]</span><span class="se">\n</span><span class="s1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#39;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-16"><code data-line-number="16"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-16" name="rest_code_5defa6b60bf345e8ac072f136362524b-16"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-17"><code data-line-number="17"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-17" name="rest_code_5defa6b60bf345e8ac072f136362524b-17"></a><span class="k">def</span><span class="w"> </span><span class="nf">write</span><span class="p">(</span><span class="n">has_jsonproperty</span><span class="p">,</span> <span class="n">vtype</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/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-18"><code data-line-number="18"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-18" name="rest_code_5defa6b60bf345e8ac072f136362524b-18"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</span> <span class="n">has_jsonproperty</span><span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-19"><code data-line-number="19"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-19" name="rest_code_5defa6b60bf345e8ac072f136362524b-19"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">jsonproperty</span> <span class="o">=</span> <span class="n">JSONPROPERTY_TEMPLATE</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-20"><code data-line-number="20"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-20" name="rest_code_5defa6b60bf345e8ac072f136362524b-20"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">else</span><span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-21"><code data-line-number="21"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-21" name="rest_code_5defa6b60bf345e8ac072f136362524b-21"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">jsonproperty</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-22"><code data-line-number="22"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-22" name="rest_code_5defa6b60bf345e8ac072f136362524b-22"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="n">TEMPLATE</span> <span class="o">%</span> <span class="p">(</span><span class="n">jsonproperty</span><span class="p">,</span> <span class="n">vtype</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">name</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/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-23"><code data-line-number="23"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-23" name="rest_code_5defa6b60bf345e8ac072f136362524b-23"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-24"><code data-line-number="24"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-24" name="rest_code_5defa6b60bf345e8ac072f136362524b-24"></a><span class="n">properties</span> <span class="o">=</span> <span class="p">[</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-25"><code data-line-number="25"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-25" name="rest_code_5defa6b60bf345e8ac072f136362524b-25"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;1 string name&#39;</span><span class="p">,</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-26"><code data-line-number="26"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-26" name="rest_code_5defa6b60bf345e8ac072f136362524b-26"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="s1">&#39;0 int another&#39;</span><span class="p">,</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-27"><code data-line-number="27"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-27" name="rest_code_5defa6b60bf345e8ac072f136362524b-27"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1"># 12 fields omitted for brevity</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-28"><code data-line-number="28"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-28" name="rest_code_5defa6b60bf345e8ac072f136362524b-28"></a><span class="p">]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-29"><code data-line-number="29"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-29" name="rest_code_5defa6b60bf345e8ac072f136362524b-29"></a><span class="n">properties_split</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span><span class="o">.</span><span class="n">split</span><span class="p">()</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">properties</span><span class="p">]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-30"><code data-line-number="30"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-30" name="rest_code_5defa6b60bf345e8ac072f136362524b-30"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-31"><code data-line-number="31"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-31" name="rest_code_5defa6b60bf345e8ac072f136362524b-31"></a><span class="c1"># Private definitions (internal)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-32"><code data-line-number="32"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-32" name="rest_code_5defa6b60bf345e8ac072f136362524b-32"></a><span class="k">for</span> <span class="n">has_jsonproperty</span><span class="p">,</span> <span class="n">vtype</span><span class="p">,</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">properties_split</span><span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-33"><code data-line-number="33"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-33" name="rest_code_5defa6b60bf345e8ac072f136362524b-33"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2">_ { get; set; }&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">vtype</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/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-34"><code data-line-number="34"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-34" name="rest_code_5defa6b60bf345e8ac072f136362524b-34"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-35"><code data-line-number="35"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-35" name="rest_code_5defa6b60bf345e8ac072f136362524b-35"></a><span class="nb">print</span><span class="p">()</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-36"><code data-line-number="36"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-36" name="rest_code_5defa6b60bf345e8ac072f136362524b-36"></a><span class="c1"># Public definitions (with notifications)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-37"><code data-line-number="37"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-37" name="rest_code_5defa6b60bf345e8ac072f136362524b-37"></a><span class="k">for</span> <span class="n">has_jsonproperty</span><span class="p">,</span> <span class="n">vtype</span><span class="p">,</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">properties_split</span><span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_5defa6b60bf345e8ac072f136362524b-38"><code data-line-number="38"></code></a></td><td class="code"><code><a id="rest_code_5defa6b60bf345e8ac072f136362524b-38" name="rest_code_5defa6b60bf345e8ac072f136362524b-38"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">print</span><span class="p">(</span><span class="n">write</span><span class="p">(</span><span class="n">has_jsonproperty</span> <span class="o">==</span> <span class="s1">&#39;1&#39;</span><span class="p">,</span> <span class="n">vtype</span><span class="p">,</span> <span class="n">name</span><span class="p">))</span>
</code></td></tr></table></div><p>That script takes a list of properties and spits out a block of code, ready to
be pasted into the code. Visual Studio has a nice <em>Insert File as Text</em>
feature, so redirecting the output to a file and using that option is enough.</p>
</section>
<section id="episode-ii-fixing-argument-order-or-vim-re-writing-java">
<h1>Episode II: Fixing argument order, or Vim (re)writing Java</h1>
<p>Another project, <a class="reference external" href="https://github.com/Kwpolska/numbernamer">Number Namer</a>, written in Java, and it does what it says on
the tin: takes a number and writes it out as words, while being multilingual and
extensible. I used Eclipse for this project, because it looks good, is really
helpful with its code linting, and does not run slowly on my aging system (I’m
looking at you, IntelliJ IDEA aka PyCharm aka Android Studio…)</p>
<p>And so, I was building a test suite, using <a class="reference external" href="http://junit.org/">JUnit</a>. It’s pretty
straightforward, and I remember the syntax from Python’s unittest (even though
I write tests with <a class="reference external" href="http://pytest.org/">pytest</a> nowadays). Or so I thought.</p>
<div class="code"><pre class="code java"><a id="rest_code_4fd345f92b594e8bb7094164392eebcd-1" name="rest_code_4fd345f92b594e8bb7094164392eebcd-1" href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4fd345f92b594e8bb7094164392eebcd-1"></a><span class="c1">// (incorrect)</span>
<a id="rest_code_4fd345f92b594e8bb7094164392eebcd-2" name="rest_code_4fd345f92b594e8bb7094164392eebcd-2" href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4fd345f92b594e8bb7094164392eebcd-2"></a><span class="n">assertEquals</span><span class="p">(</span><span class="s">&quot;Basic integers (7) failed&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">namer</span><span class="p">.</span><span class="na">name</span><span class="p">(</span><span class="mi">7L</span><span class="p">),</span><span class="w"> </span><span class="s">&quot;seven&quot;</span><span class="p">);</span>
<a id="rest_code_4fd345f92b594e8bb7094164392eebcd-3" name="rest_code_4fd345f92b594e8bb7094164392eebcd-3" href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4fd345f92b594e8bb7094164392eebcd-3"></a><span class="c1">// (fixed)                              ^ cursor</span>
<a id="rest_code_4fd345f92b594e8bb7094164392eebcd-4" name="rest_code_4fd345f92b594e8bb7094164392eebcd-4" href="https://chriswarrick.com/blog/2016/05/27/code-writing-code-python-and-vim/#rest_code_4fd345f92b594e8bb7094164392eebcd-4"></a><span class="n">assertEquals</span><span class="p">(</span><span class="s">&quot;Basic integers (7) failed&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;seven&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">namer</span><span class="p">.</span><span class="na">name</span><span class="p">(</span><span class="mi">7L</span><span class="p">));</span>
</pre></div>
<p>You see, the typical Python spelling is <code class="docutils literal">self.assertEquals(actual,
expected)</code>. Java adds a <code class="docutils literal">String</code> message parameter and it also swaps
<code class="docutils literal">actual</code> and <code class="docutils literal">expected</code>. Which I didn’t notice at first, and I wrote my
assertions incorrectly. While it doesn’t <em>really</em> matter (it will still work),
the output looked a bit weird.</p>
<p>And I noticed only when I finished writing my tests (and I had a typo in my
expected output). I wanted to fix them all — not manually, of course. So, I
closed this file, brought up Vim, searched for the motion I need (it’s
<code class="docutils literal">t{char}</code> — see <code class="docutils literal">:help t</code>). And I ended up with this
(cursor placed on the comma after the first argument):</p>
<div style="text-align: center;">
<kbd style="font-size: 2em;">dt,</kbd><kbd style="font-size: 2em;">t)</kbd><kbd style="font-size: 2em;">p</kbd>
</div><p>What does this do, you may ask? It’s actually pretty self-explanatory:</p>
<blockquote><p>
<b>d</b>elete <b>t</b>ill comma, (go) <b>t</b>ill closing parenthesis, <b>p</b>aste.
</p></blockquote><p>This fixes one line. Automatically. Make it a macro (wrap in <code class="docutils literal">qq</code> … <code class="docutils literal">q</code>,
use with <code class="docutils literal">&#64;q</code>) and now you can run it on all lines, either by moving manually or by
searching for <code class="docutils literal">,</code> and pressing <code class="docutils literal">n&#64;q</code> until you run out of lines.</p>
</section>
<section id="epilogue">
<h1>Epilogue</h1>
<p>Some of you might say “but VS/Eclipse/IDEA has an option for that somewhere” or
“[expensive tool] can do that” — and a Google search shows that there is an
Eclipse plugin to swap arguments and that I could also write a regex to solve
my second issue. Nevertheless, Python is a great tool in a programmer’s toolbox
— especially the interactive interpreter. And Vim is an awesome editor that can
accomplish magic in a few keystrokes — and there are many more things you can
do with it.</p>
<p class="lead">Go learn <a class="reference external" href="https://www.python.org/">Python</a> and <a class="reference external" href="http://www.vim.org/">Vim</a> now.</p>
<p>Also: don’t even bother with VsVim or IdeaVim or any other Vim emulation
plugins, they work in unusual ways and often don’t give you everything — for
example, VsVim has a Vim visual mode (<code class="docutils literal">v</code> key) and Visual Studio selection
mode (mouse), and only one allows Vim keystrokes (the other will replace
selected text).</p>
</section>
]]></content:encoded><category>Programming</category><category>C#</category><category>Java</category><category>programming</category><category>Python</category><category>Vim</category></item></channel></rss>