<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Vegard Stikbakke</title>
    <link>https://vegardstikbakke.com/</link>
    <description>Software engineer from Norway</description>
    <language>en-us</language>
    <lastBuildDate>Tue, 24 Mar 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://vegardstikbakke.com/feed.xml" rel="self" type="application/rss+xml"></atom:link>
    <item>
      <title>Detecting file changes on macOS with kqueue</title>
      <link>https://vegardstikbakke.com/kqueue/</link>
      <pubDate>Tue, 24 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/kqueue/</guid>
      <description>&lt;p&gt;A while ago I wrote &lt;a href=&#34;https://github.com/vegarsti/reload&#34;&gt;a small file watcher in Go for my own use&lt;/a&gt; with an &lt;a href=&#34;/file-watcher&#34;&gt;accompanying blog post&lt;/a&gt;.&#xA;I needed a tool that I could just plop in front of the command I was running as part of my iteration loop.&#xA;I use it for recompiling C files when I modify them,&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;reload gcc main.c -o main &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and for rebuilding and reloading my static site on file changes.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;reload make&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It has two modes.&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;If one or more explicit files are mentioned in the command, it will watch those files.&lt;/li&gt;&#xA;&lt;li&gt;If it does not find any filenames, it will watch all files in the working directory.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The only thing &lt;code&gt;reload&lt;/code&gt; needs to know is whether any file it is watching has changed.&#xA;If a file has changed, it reruns the command.&lt;/p&gt;&#xA;&lt;p&gt;It works great! But I copped out on the part that was the most unfamiliar to me, namely &lt;a href=&#34;/file-watcher/#detecting-file-changes&#34;&gt;detecting file changes&lt;/a&gt;.&#xA;I used the &lt;a href=&#34;https://github.com/fsnotify/fsnotify&#34;&gt;fsnotify&lt;/a&gt; package which is a nice cross-platform Go library for this.&#xA;It supports macOS as well as Linux but since it&#39;s for my own use, I don&#39;t care about Linux support.&#xA;More importantly, I wanted to understand what fsnotify did under the hood.&lt;/p&gt;&#xA;&lt;p&gt;On macOS it uses the &lt;a href=&#34;https://en.wikipedia.org/wiki/Kqueue&#34;&gt;kqueue&lt;/a&gt; event notification interface.&#xA;Let&#39;s take a look at how this works, writing some C code to test it, and then finally implement it in the reload Go program.&lt;/p&gt;&#xA;&lt;h2 id=&#34;kqueue-data-structures&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#kqueue-data-structures&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&lt;code&gt;kqueue&lt;/code&gt; data structures&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;code&gt;kqueue()&lt;/code&gt; &lt;a href=&#34;https://man.freebsd.org/cgi/man.cgi?kqueue&#34;&gt;function call&lt;/a&gt; creates a new kernel event queue (a kqueue) and returns a file descriptor.&#xA;We register and wait for system events using the &lt;code&gt;kevent()&lt;/code&gt; function call, which uses the &lt;code&gt;kevent&lt;/code&gt; data structure.&#xA;This has five fields that we need to care about.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;ident&lt;/code&gt; The source of the event. In this case, it will be a file descriptor for the file we want to watch.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;filter&lt;/code&gt; The kernel filter used to process the event.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;flags&lt;/code&gt; Actions to perform on the event.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;fflags&lt;/code&gt; Filter-specific flags.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;udata&lt;/code&gt; Opaque user data identifier. We&#39;ll use this to store the filename for easy lookup later.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Well, which kernel filter do we use if we want to watch a file for changes?&#xA;There are 9 possible filters, but the one we&#39;re looking for is &lt;code&gt;EVFILT_VNODE&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;EVFILT_VNODE   Takes a file descriptor as the identifier and the events to watch for in fflags,&#xA;               and returns when one or more of the requested events occurs on the descriptor.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It goes on to list 10 possible events that can be used in &lt;code&gt;fflags&lt;/code&gt;, but the only flag we need is &lt;code&gt;NOTE_WRITE&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;NOTE_WRITE     A write occurred on the file referenced by the descriptor.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Finally, &lt;code&gt;flags&lt;/code&gt; defines the actions to perform on the event.&#xA;There are 10 possible flags, but we only need &lt;code&gt;EV_ADD&lt;/code&gt; to register the event in the &lt;code&gt;kqueue&lt;/code&gt;, and &lt;code&gt;EV_CLEAR&lt;/code&gt; to reset the event state after delivery.&#xA;Without &lt;code&gt;EV_CLEAR&lt;/code&gt;, we&#39;d get the first file change again and again - try it!&lt;/p&gt;&#xA;&lt;p&gt;If there are multiple writes to this file before we retrieve the event from the kqueue, these will be collapsed into one event.&#xA;However, if multiple files are written, these will be distinct events.&#xA;So in practice, in &lt;code&gt;reload&lt;/code&gt;, we&#39;ll likely want a window such that we don&#39;t rerun the command on every single event if they are close in time.&lt;/p&gt;&#xA;&lt;p&gt;We now have all we need to initialize a kevent structure.&lt;/p&gt;&#xA;&lt;h2 id=&#34;watching-named-files&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#watching-named-files&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Watching named files&lt;/h2&gt;&#xA;&lt;p&gt;Let&#39;s write a program to watch all files passed as arguments.&#xA;We omit error handling for rare errors for brevity, but keep for stuff that might happen such as trying to open a file that doesn&#39;t exist.&lt;/p&gt;&#xA;&lt;p&gt;First we open the files to watch with &lt;code&gt;O_EVTONLY&lt;/code&gt;.&#xA;The &lt;code&gt;open&lt;/code&gt; man pages say the event-only mode is only intended for monitoring a file for changes, such as with &lt;code&gt;kqueue&lt;/code&gt;.&#xA;We&#39;ll need the following headers:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;sys/types.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;sys/event.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;sys/time.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We create an array of kevent structs representing the changes we care about,&#xA;namely writes to files provided on the command-line.&#xA;We use the &lt;code&gt;EV_SET&lt;/code&gt; macro to initialize the kevent struct.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Open all files and set up change events&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nfiles&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;argc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fds&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nfiles&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kevent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;changes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nfiles&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kevent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nfiles&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;fds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;argv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;O_EVTONLY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;fprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;open(%s): &lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;argv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;EV_SET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;changes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;fds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;EVFILT_VNODE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;EV_ADD&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EV_CLEAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;NOTE_WRITE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;argv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// udata: opaque user data. Store filename here.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We register the events with a call to &lt;code&gt;kevent()&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Register all events at once&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kq&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kqueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;kevent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;kq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// the queue&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;changes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// array of kevent events to register&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;nfiles&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// length of array&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// struct to populate with event (not used here)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// number of events to wait for&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// timeout if waiting for event (irrelevant here)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&#39;re now ready to start an event loop and listen to file changes.&#xA;We use &lt;code&gt;kevent()&lt;/code&gt; for this as well.&#xA;After we get an event back, we can look at &lt;code&gt;event.fflags&lt;/code&gt; to see which file event was emitted.&#xA;In our case we&#39;re only listening for &lt;code&gt;NOTE_WRITE&lt;/code&gt; so that should always be true.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kevent&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;kevent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;kq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// queue&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// array of events to register&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// no events to register&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// struct to populate with event&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// number of events to wait for&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// no timeout; wait forever&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fflags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NOTE_WRITE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;udata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;[%s] written&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The full code for this is in &lt;a href=&#34;https://gist.github.com/vegarsti/2fca3bc234839b1ccb3bc33ff03e535d&#34;&gt;this GitHub Gist&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;watching-a-directory&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#watching-a-directory&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Watching a directory&lt;/h2&gt;&#xA;&lt;p&gt;In the second mode of reload, we watch the current working directory for any file changes.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;reload make&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&#39;s first look at how to watch a directory.&#xA;First we open the directory itself, and we watch it like we did above for a single file.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;directory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;O_EVTONLY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This emits events for new files added to the directory and for file deletions.&#xA;Such changes involve writing to the file on disk that represents the directory.&lt;/p&gt;&#xA;&lt;p&gt;However, this does not emit events when there are changes to an existing file, so it&#39;s not sufficient for our use.&#xA;We need to open all files within the directory and watch these for changes individually!&#xA;This means that when a file is created, we need to add this file to be watched.&lt;/p&gt;&#xA;&lt;p&gt;I wrote the reload program in Go, so we now switch over to looking at Go code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;implementing-in-go&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#implementing-in-go&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Implementing in Go&lt;/h2&gt;&#xA;&lt;p&gt;We need a reference to a kqueue, and we need to keep track of which file descriptors we open.&#xA;We also want to be able to refer to files by path.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;kq&lt;/span&gt;      &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// kqueue file descriptor&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fds&lt;/span&gt;     &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// path -&amp;gt; file descriptor mapping&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fdPaths&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// file descriptor -&amp;gt; path mapping (reverse lookup)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice the CloseOnExec call when we create the kqueue below.&#xA;When &lt;code&gt;reload&lt;/code&gt; re-runs your command, Go&#39;s exec package uses the fork + exec pattern.&#xA;A fork clones the parent&#39;s open file descriptors (fd) into the child process,&#xA;including our kqueue fd and every file descriptor we have opened for watching.&#xA;The child process doesn&#39;t need these, and leaking them can cause subtle problems.&#xA;The watched files can&#39;t be fully deleted by the operating system since their reference count never hits zero, and the child holds a kqueue it never drains.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;O_CLOEXEC&lt;/code&gt; flag tells the kernel to automatically close these file descriptors when exec runs.&#xA;We set it on the kqueue itself here, and on each watched file in &lt;code&gt;Add&lt;/code&gt; below.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;newWatcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;kq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Kqueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Set close-on-exec flag so child processes don&amp;#39;t inherit the kqueue fd&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;CloseOnExec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;kq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;kq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;kq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;fds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;     &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;fdPaths&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When adding a file to be watched, we open the file and register for file writes on the kqueue.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Skip if already watching&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exists&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exists&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Open file with event-only flag to only get events,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// and close on exec, so that exec&amp;#39;ing the command to reload&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// does not copy the file descriptors.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;O_EVTONLY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;O_CLOEXEC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Register file/directory&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fd&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fdPaths&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Kevent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;kq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;c1&#34;&gt;// changes to look for&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Kevent_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;Ident&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;nb&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;Filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EVFILT_VNODE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;Flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EV_ADD&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EV_CLEAR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;Fflags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;uint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;NOTE_WRITE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// events to populate: none here&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// no timeout; not populating events anyway&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To add all files in a directory, we walk the directory tree and call &lt;code&gt;Add&lt;/code&gt; for each entry, be it a file or nested directory.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;addRecursive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Stat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// If it&amp;#39;s a file, watch it directly&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;IsDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Walk the directory tree and add all directories and files&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;filepath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WalkDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;walkPath&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;DirEntry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;c1&#34;&gt;// non-nil when WalkDir fails to access walkPath&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;c1&#34;&gt;// Watch the directory for new files and deletions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;walkPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we set up the event loop where we listen on the kqueue until we get a file event.&#xA;Once we do, we return back so the main program can reload the command.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Wait until any file changes, and return the filename of the changed file&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Wait&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;events&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Kevent_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Kevent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;kq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// the queue&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// changes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// events to populate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// no timeout&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;EINTR&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;w&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fdPaths&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;events&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ident&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As I mentioned, if a file is created in a directory, we need to add that file to be watched as well.&#xA;We solve that by re-walking the directory if the file event comes from a directory and not a file.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// If the file event was from a directory, it may have&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// created a new file. If so, we need to add it!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Stat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;IsDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Re-walk to pick up new files&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;addRecursive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that we don&#39;t remove a file if it was deleted.&#xA;This means we are leaking file descriptors when files are deleted, and causing the OS to be unable to clean these up.&#xA;But for my use case it&#39;s so rare that I both delete a file and need to keep the program running for a long time that we don&#39;t cover this here.&lt;/p&gt;&#xA;&lt;h2 id=&#34;final-thoughts&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#final-thoughts&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Final thoughts&lt;/h2&gt;&#xA;&lt;p&gt;This was fun!&#xA;I did not know about kqueue before, but I learned that it&#39;s easy to work with, and can be used for many inter process communication use cases on macOS.&lt;/p&gt;&#xA;&lt;p&gt;Another way to solve the file watching problem would be the naive way, that is, to poll for changes.&#xA;Since kqueue requires an open fd per watched file, kqueue won&#39;t scale to very large directory trees.&#xA;The third way this could be done on macOS is to use &lt;a href=&#34;https://en.wikipedia.org/wiki/FSEvents&#34;&gt;FSEvents&lt;/a&gt;, which does not have the problem of fd exhaustion.&lt;/p&gt;&#xA;&lt;p&gt;I would be very happy if you checked out &lt;a href=&#34;https://github.com/vegarsti/reload&#34;&gt;the reload code on GitHub&lt;/a&gt;, tried it out on your Mac, or reached out to me with any thoughts or experiences doing something similar.&lt;/p&gt;&#xA;&lt;p&gt;Finally, if you&#39;re interested in reading more about kqueue,&#xA;you can read &lt;a href=&#34;https://nima101.github.io/kqueue_server&#34;&gt;this post&lt;/a&gt; about implementing a bidirectional streaming server using kqueue in C,&#xA;and &lt;a href=&#34;https://people.freebsd.org/~jlemon/papers/kqueue.pdf&#34;&gt;the original paper&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Recently</title>
      <link>https://vegardstikbakke.com/recently-2026-1/</link>
      <pubDate>Wed, 04 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/recently-2026-1/</guid>
      <description>&lt;p&gt;I want to write more!&#xA;I like the format of &lt;a href=&#34;https://macwright.com/&#34;&gt;Tom MacWright&lt;/a&gt;&#39;s Recently posts.&#xA;So here&#39;s what I&#39;ve been doing and thinking about recently, for the first month of 2026.&lt;/p&gt;&#xA;&lt;h3 id=&#34;family&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#family&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Family&lt;/h3&gt;&#xA;&lt;p&gt;My wife and I just had our third child at the start of this year - our third son!&#xA;The older boys are 5 and 3, and they are very proud to have a baby brother.&#xA;I have to say, it&#39;s so lovely to have a baby!&#xA;And family life with three kids has not been so much different from two so far.&lt;/p&gt;&#xA;&lt;h3 id=&#34;reading&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#reading&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Reading&lt;/h3&gt;&#xA;&lt;p&gt;I reread &lt;a href=&#34;https://en.wikipedia.org/wiki/The_Years_of_Lyndon_Johnson#Book_One:_The_Path_to_Power_(1982)&#34;&gt;The Path to Power&lt;/a&gt;, the first Lyndon Johnson book by Robert Caro.&#xA;It&#39;s an absolutely incredible book.&#xA;The combination of LBJ&#39;s life being incredibly dramatic, and him working incredibly hard;&#xA;unprecedented levels of research by Caro and his wife;&#xA;and Caro&#39;s stellar writing.&#xA;Chapters are thematic and have excellent narrative arcs, making for really great reading.&lt;/p&gt;&#xA;&lt;h3 id=&#34;programming&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#programming&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Programming&lt;/h3&gt;&#xA;&lt;p&gt;The biggest shift in programming in the past year is the rise of &lt;a href=&#34;/new-site-2025&#34;&gt;agentic coding&lt;/a&gt;.&#xA;Having an agent (a program using an LLM) write code for me is incredibly fun!&#xA;I made a spelling game for my oldest in an hour that has given us hours of fun.&lt;/p&gt;&#xA;&lt;blockquote class=&#34;twitter-tweet&#34; data-dnt=&#34;true&#34; align=&#34;center&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;My oldest has almost learned to read. This morning I made a macOS game for him: It shows a random &amp;lt;5 letter word and you have to speak the word out loud. It was a great success! &lt;a href=&#34;https://t.co/xFbamJhJ33&#34;&gt;pic.twitter.com/xFbamJhJ33&lt;/a&gt;&lt;/p&gt;&amp;mdash; Vegard Stikbakke (@vegardstikbakke) &lt;a href=&#34;https://twitter.com/vegardstikbakke/status/2008567173195985209?ref_src=twsrc%5Etfw&#34;&gt;January 6, 2026&lt;/a&gt;&lt;/blockquote&gt;&#xA;&lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;&#xA;&lt;p&gt;For the past weeks I&#39;ve switched from Claude Code by Anthropic to using the &lt;a href=&#34;https://github.com/badlogic/pi-mono&#34;&gt;pi agent&lt;/a&gt;, which Armin Ronacher wrote nicely about &lt;a href=&#34;https://lucumr.pocoo.org/2026/1/31/pi/&#34;&gt;here&lt;/a&gt;.&#xA;Pros compared to Claude Code:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;It&#39;s minimal, in a good way.&lt;/li&gt;&#xA;&lt;li&gt;It doesn&#39;t ask for permission at all (yes you can do that with Claude Code, too).&lt;/li&gt;&#xA;&lt;li&gt;It&#39;s open source, extensible, and sessions can easily be shared.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;watching&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#watching&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Watching&lt;/h3&gt;&#xA;&lt;p&gt;My wife and I got completely hooked on &lt;a href=&#34;https://en.wikipedia.org/wiki/The_Pitt&#34;&gt;The Pitt&lt;/a&gt;, a medical drama series.&#xA;We binged season 1 as fast as we could.&#xA;It&#39;s a medical drama series which I was sure I was too high brow to like, but it was so fun.&#xA;It did win five Emmys last year!&#xA;One season spans one shift at the ICU, so it&#39;s real-time, which is a very neat format.&lt;/p&gt;&#xA;&lt;p&gt;In the evenings when my wife is sleeping and I&#39;m soothing the baby, I&#39;ve been watching &lt;a href=&#34;https://en.wikipedia.org/wiki/Veep&#34;&gt;Veep&lt;/a&gt;.&#xA;It&#39;s laugh out loud funny.&#xA;Great characters, great dialogue.&#xA;Turns out Jesse Armstrong of &lt;a href=&#34;https://en.wikipedia.org/wiki/Succession_(TV_series)&#34;&gt;Succession&lt;/a&gt; fame is one of the writers.&#xA;Succession is possibly the best and funniest TV series I&#39;ve seen, so I&#39;m not surprised!&lt;/p&gt;&#xA;&lt;h3 id=&#34;work&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#work&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Work&lt;/h3&gt;&#xA;&lt;img src=&#34;/dune-cap.jpeg&#34; alt=&#34;alt text&#34; width=&#34;500&#34;&gt;&#xA;&lt;p&gt;Next week is my last at &lt;a href=&#34;https://dune.com/&#34;&gt;Dune&lt;/a&gt;, where I&#39;ve worked for five years.&#xA;When I joined I was the fourth person in the company, we&#39;re now more than 60.&#xA;We raised a &lt;a href=&#34;https://dune.com/blog/series-a&#34;&gt;Series A&lt;/a&gt;, a &lt;a href=&#34;https://dune.com/blog/series-b&#34;&gt;Series B&lt;/a&gt;.&#xA;I worked with so many incredibly kind and smart coworkers.&#xA;It&#39;s been very fun, and I&#39;ve learned a ton.&#xA;But now it&#39;s time to try something new!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>How I rewrote my site using Claude Code</title>
      <link>https://vegardstikbakke.com/new-site-2025/</link>
      <pubDate>Wed, 31 Dec 2025 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/new-site-2025/</guid>
      <description>&lt;p&gt;This site is now built using a &lt;a href=&#34;https://github.com/vegarsti/vegardstikbakke.com&#34;&gt;static site builder&lt;/a&gt; that Claude Code wrote for me.&#xA;Iterating on the site is so much fun now!&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;/claude-commit.png&#34; alt=&#34;Claude commit&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;If you&#39;re a software engineer and not currently using AI for coding, I hope this post can entice you into giving it a try.&#xA;You might be underestimating how good it is!&lt;/p&gt;&#xA;&lt;h2 id=&#34;previous-version&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#previous-version&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Previous version&lt;/h2&gt;&#xA;&lt;p&gt;The site used to be built using &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt; which is a popular, feature complete static site builder.&#xA;I had found a template I liked and modified it, and it produced the HTML I needed for my Markdown content.&#xA;But I still found it very complicated to make the changes I wanted.&#xA;Writing my own static site builder was definitely an option, but I had considered it too cumbersome.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-use-ai&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#why-use-ai&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Why use AI&lt;/h2&gt;&#xA;&lt;p&gt;Meanwhile, it&#39;s the end of 2025 and AI tooling for software engineers did so many leaps forward this year that it&#39;s hard to fathom.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&#xA;I&#39;m now regularly using agents at work to write code.&#xA;What&#39;s an agent, you ask?&#xA;&lt;strong&gt;An LLM agent runs tools in a loop to achieve a goal&lt;/strong&gt; (&lt;a href=&#34;https://simonwillison.net/2025/Sep/18/agents/&#34;&gt;via Simon Willison&lt;/a&gt;).&#xA;I use agents for:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Writing new application code&lt;/li&gt;&#xA;&lt;li&gt;Writing tests&lt;/li&gt;&#xA;&lt;li&gt;Modifying existing code&lt;/li&gt;&#xA;&lt;li&gt;Debugging&lt;/li&gt;&#xA;&lt;li&gt;Finding terminal commands (so instead of googling)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Writing a simple static site builder for this small blog is a perfect task for a coding agent, as they are especially good at starting from scratch.&#xA;I could get a new site done a lot quicker than by writing it all by myself.&#xA;I could even do it in a few minutes while having some downtime at work.&lt;/p&gt;&#xA;&lt;p&gt;Let&#39;s look at how we built it.&lt;/p&gt;&#xA;&lt;!--&#xA;&#xA;TODO: Write more about the experience!&#xA;&#xA;For me, I want to hear more about your experience. How long did it take you? How many words did you need to type? How many messages? How does this compare to not using agents?&#xA;&#xA;Did it go exactly as you expected it to? Have you done this kinda of project before?&#xA;&#xA;What are you putting into the prompt and _not_ putting into the prompt?&#xA;&#xA;Perhaps you need to consider who your target reader is more. If it&#39;s someone who is new to agents/LLMs, you should introduce more of the concepts.&#xA;&#xA;How did you verify (or, how did Claude verify) that slug links weren&#39;t broken?&#xA;&#xA;--&gt;&#xA;&lt;h2 id=&#34;first-claude-session&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#first-claude-session&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;First Claude session&lt;/h2&gt;&#xA;&lt;p&gt;I wanted to have a program that was able to build the HTML for my existing blog posts.&#xA;Claude did this in the initial session which spanned at most 10 minutes.&#xA;I wrote 153 words in total across 6 messages:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Initial prompt&lt;/li&gt;&#xA;&lt;li&gt;3 sets of clarifying questions before it started to write code&lt;/li&gt;&#xA;&lt;li&gt;2 more follow up messages from me after it was done writing the code&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;To start, I created a new directory and copied over my existing blog posts.&#xA;I opened &lt;code&gt;claude&lt;/code&gt; and gave it this prompt:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;I want to create a static site from scratch.&#xA;The code to build it needs to be in Go.&#xA;I want it to be very minimal:&#xA;The site builder needs to be able to take md blog posts and embed them.&#xA;I have posts in the content repo.&#xA;Their slugs need to be preserved.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I wanted to use Go because it&#39;s simple and I know it well, the standard library has great HTML templating support, and the developer tooling is excellent.&#xA;Claude replied:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ I&#39;ll help you create a minimal static site generator in Go for your blog posts. Let me first explore the current state of your repository to understand what exists.&#xA;&#xA;⏺ Explore(Explore repository structure) Haiku 3.5&#xA;  ⎿  Done (6 tool uses · 12.8k tokens · 28s)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;By pressing Shift + Tab twice I had set Claude in &lt;a href=&#34;https://code.claude.com/docs/en/common-workflows#use-plan-mode-for-safe-code-analysis&#34;&gt;Plan Mode&lt;/a&gt; which will first create an implementation plan.&#xA;The plan is a markdown file persisted to disk.&#xA;See &lt;a href=&#34;https://lucumr.pocoo.org/2025/12/17/what-is-plan-mode/&#34;&gt;this blog post by Armin Ronacher&lt;/a&gt; for a good investigation of how Plan Mode works.&#xA;It will usually ask clarifying questions until it decides the plan is clear.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ Now I need to clarify some details about your requirements:&#xA;&#xA;⏺ User answered Claude&#39;s questions:&#xA;  ⎿  · What should the generated site structure include? → Just a bio blurb,&#xA;      and a header which has links &amp;quot;Vegard Stikbakke&amp;quot; (links back to the&#xA;     bio, &amp;quot;Posts&amp;quot;, and &amp;quot;Books&amp;quot;&#xA;     · How should post slugs be determined? → From frontmatter field&#xA;     · Where should the generated HTML files be output? → public/ directory&#xA;     · Do you want any additional features? → Draft post filtering&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It asked a few more questions, 8 in total.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ Perfect! Now I&#39;ll create the final implementation plan:&#xA;&#xA;⏺ Updated plan&#xA;  ⎿  /plan to preview&#xA;&#xA;⏺ User approved Claude&#39;s plan&#xA;  ⎿  Plan saved to: ~/.claude/plans/hashed-snuggling-codd.md · /plan to edit&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Once I confirmed the implementation plan, it started to execute on the plan, reading it back from disk.&#xA;The complete plan:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;     Static Site Generator in Go - Implementation Plan&#xA;&#xA;     Overview&#xA;&#xA;     Build a minimal static site generator in Go that converts markdown blog&#xA;      posts to HTML, preserving slugs from frontmatter and filtering drafts.&#xA;&#xA;     Requirements Summary&#xA;&#xA;     - Homepage: Bio from content/about.md&#xA;     - Navigation header: &amp;quot;Vegard Stikbakke&amp;quot; (home) | &amp;quot;Posts&amp;quot; | &amp;quot;Books&amp;quot;&#xA;     - Posts listing: All blog posts (excluding drafts)&#xA;     - Books listing: Placeholder data with metadata (title, author, date&#xA;     read, rating, summary)&#xA;     - Individual post pages: /blog/{slug}/index.html&#xA;     - Output directory: public/&#xA;     - Filter posts with draft: true&#xA;     - Preserve slugs from frontmatter slug field&#xA;&#xA;     File Structure&#xA;&#xA;     personal-site-static/&#xA;     ├── main.go              # Entry point, orchestrates build&#xA;     ├── types.go             # Data structures (Post, Page, Book, Site)&#xA;     ├── parser.go            # Markdown parsing and frontmatter extraction&#xA;     ├── templates.go         # HTML templates&#xA;     ├── generator.go         # HTML generation logic&#xA;     ├── go.mod               # Go module definition&#xA;     ├── content/             # Input markdown files (existing)&#xA;     └── public/              # Output directory (generated)&#xA;&#xA;     Implementation Steps&#xA;&#xA;     1. Create types.go&#xA;&#xA;     Define core data structures:&#xA;     type Frontmatter struct {&#xA;         Title  string `yaml:&amp;quot;title&amp;quot;`&#xA;         Slug   string `yaml:&amp;quot;slug&amp;quot;`&#xA;         Date   string `yaml:&amp;quot;date&amp;quot;`&#xA;         Draft  bool   `yaml:&amp;quot;draft&amp;quot;`&#xA;     }&#xA;&#xA;     type Post struct {&#xA;         Title       string&#xA;         Slug        string&#xA;         Date        time.Time&#xA;         DateString  string&#xA;         Draft       bool&#xA;         HTMLContent string&#xA;     }&#xA;&#xA;     type Page struct {&#xA;         Title       string&#xA;         HTMLContent string&#xA;     }&#xA;&#xA;     type Book struct {&#xA;         Title      string&#xA;         Author     string&#xA;         DateRead   string&#xA;         Rating     int&#xA;         Summary    string&#xA;     }&#xA;&#xA;     type Site struct {&#xA;         Posts      []Post&#xA;         AboutPage  Page&#xA;         Books      []Book&#xA;     }&#xA;&#xA;     2. Create parser.go&#xA;&#xA;     Implement markdown and frontmatter parsing:&#xA;     - extractFrontmatter(): Split YAML frontmatter from content&#xA;     - parsePost(): Parse individual blog post files&#xA;     - loadPosts(): Load all posts from content/blog/&#xA;     - loadPage(): Load static pages (about.md)&#xA;     - parseDate(): Handle multiple date formats (quoted, unquoted, ISO&#xA;     8601)&#xA;     - filterPublished(): Remove drafts from post list&#xA;     - sortPostsByDate(): Sort posts newest first&#xA;&#xA;     Dependencies:&#xA;     - github.com/yuin/goldmark - Markdown to HTML conversion&#xA;     - gopkg.in/yaml.v3 - YAML frontmatter parsing&#xA;&#xA;     3. Create templates.go&#xA;&#xA;     Define HTML templates using html/template:&#xA;     - Base layout with navigation header&#xA;     - Homepage template (bio content)&#xA;     - Posts listing template&#xA;     - Books listing template&#xA;     - Individual post template&#xA;     - Minimal inline CSS for clean styling&#xA;&#xA;     Navigation structure (on all pages):&#xA;     &amp;lt;nav&amp;gt;&#xA;       &amp;lt;a href=&amp;quot;/&amp;quot;&amp;gt;Vegard Stikbakke&amp;lt;/a&amp;gt;&#xA;       &amp;lt;a href=&amp;quot;/posts/&amp;quot;&amp;gt;Posts&amp;lt;/a&amp;gt;&#xA;       &amp;lt;a href=&amp;quot;/books/&amp;quot;&amp;gt;Books&amp;lt;/a&amp;gt;&#xA;     &amp;lt;/nav&amp;gt;&#xA;&#xA;     4. Create generator.go&#xA;&#xA;     Implement HTML generation functions:&#xA;     - generateHomepage(): Create /index.html with about.md content&#xA;     - generatePostsListing(): Create /posts/index.html with all published&#xA;     posts&#xA;     - generateBooksListing(): Create /books/index.html with placeholder&#xA;     book data&#xA;     - generateIndividualPosts(): Create /blog/{slug}/index.html for each&#xA;     post&#xA;     - renderToFile(): Helper to render template and write to file&#xA;&#xA;     5. Create main.go&#xA;&#xA;     Wire everything together:&#xA;     1. Load posts from content/blog/&#xA;     2. Filter out drafts&#xA;     3. Sort posts by date (newest first)&#xA;     4. Load about.md page&#xA;     5. Create placeholder book data (3-5 sample books)&#xA;     6. Clean and recreate public/ directory&#xA;     7. Generate all HTML files&#xA;     8. Print success message&#xA;&#xA;     6. Initialize Go module&#xA;&#xA;     go mod init github.com/vegarsti/personal-site&#xA;     go get github.com/yuin/goldmark&#xA;     go get gopkg.in/yaml.v3&#xA;&#xA;     7. Build and test&#xA;&#xA;     go build -o ssg&#xA;     ./ssg&#xA;&#xA;     Key Technical Decisions&#xA;&#xA;     1. Markdown library: goldmark - CommonMark compliant, pure Go,&#xA;     well-maintained&#xA;     2. Template system: html/template - Built-in, type-safe&#xA;     3. Date handling: Support multiple formats with flexible parsing&#xA;     4. URL structure: /blog/{slug}/index.html for clean URLs&#xA;     5. Styling: Inline CSS in templates for simplicity&#xA;     6. Draft filtering: Only filter blog posts, not static pages&#xA;&#xA;     Edge Cases Handled&#xA;&#xA;     1. Missing frontmatter fields: Error if title or slug missing, optional&#xA;      date&#xA;     2. Date formats: Support quoted strings, unquoted dates, ISO 8601&#xA;     3. Posts without dates: Still displayed but sorted to end&#xA;     4. Draft filtering: Only applies to blog posts in posts listing&#xA;     5. about.md has draft: true: Still used for homepage (not filtered)&#xA;&#xA;     Placeholder Book Data&#xA;&#xA;     Generate 3-5 sample books with structure:&#xA;     Book{&#xA;         Title: &amp;quot;Example Book&amp;quot;,&#xA;         Author: &amp;quot;Author Name&amp;quot;,&#xA;         DateRead: &amp;quot;2024-01&amp;quot;,&#xA;         Rating: 5,&#xA;         Summary: &amp;quot;Brief summary...&amp;quot;,&#xA;     }&#xA;&#xA;     Output Structure&#xA;&#xA;     public/&#xA;     ├── index.html                    # Homepage with bio&#xA;     ├── posts/&#xA;     │   └── index.html                # Posts listing&#xA;     ├── books/&#xA;     │   └── index.html                # Books listing&#xA;     └── blog/&#xA;         ├── how-do-pipes-work-sigpipe/&#xA;         │   └── index.html&#xA;         ├── csprimer/&#xA;         │   └── index.html&#xA;         └── [other slugs]/&#xA;             └── index.html&#xA;&#xA;     Critical Files to Create&#xA;&#xA;     1. /Users/vegard/dev/personal-site-static/main.go&#xA;     2. /Users/vegard/dev/personal-site-static/types.go&#xA;     3. /Users/vegard/dev/personal-site-static/parser.go&#xA;     4. /Users/vegard/dev/personal-site-static/generator.go&#xA;     5. /Users/vegard/dev/personal-site-static/templates.go&#xA;     6. /Users/vegard/dev/personal-site-static/go.mod&#xA;&#xA;     Success Criteria&#xA;&#xA;     - ✓ Single go build command produces working binary&#xA;     - ✓ Running ./ssg generates complete site in public/&#xA;     - ✓ All published posts appear in posts listing&#xA;     - ✓ Drafts are excluded from posts listing&#xA;     - ✓ Individual post pages preserve slugs from frontmatter&#xA;     - ✓ Navigation header works on all pages&#xA;     - ✓ Homepage displays about.md content&#xA;     - ✓ Books page shows placeholder book data&#xA;     - ✓ Clean, minimal styling&#xA;     - ✓ Total code under 500 lines&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It then started to write code:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ Great! Now let me implement the static site generator. I&#39;ll start by creating a todo list to track progress:&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;A typical problem for LLMs in long conversations has been to lose track of the goal, and Claude uses TODO lists to good effect.&lt;/p&gt;&#xA;&lt;p&gt;It then went on without intervention from me for several minutes, writing its planned code and trying to build the program.&#xA;It failed to build several times but was able to recover.&#xA;This is the magic of agents:&#xA;It can use a tool, such as code compilation, to autonomously figure out why it&#39;s not working and fix it.&#xA;This is a whole different world than last year when I was copy/pasting between ChatGPT and my editor.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ Bash(go build -o ssg &amp;amp;&amp;amp; ./ssg)&#xA;  ⎿  Error: Exit code 1&#xA;     # github.com/vegarsti/personal-site&#xA;     ./parser.go:60:24: filepath.Base undefined (type string has no field or&#xA;     method Base)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Eventually it finished:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ Perfect! I&#39;ve successfully built your minimal static site generator in Go. Here&#39;s what was created:&#xA;&#xA;  Generated Files&#xA;&#xA;  - types.go - Data structures (Post, Page, Book, Site)&#xA;  - parser.go - Markdown parsing and frontmatter extraction&#xA;  - templates.go - HTML templates with minimal CSS&#xA;  - generator.go - HTML generation functions&#xA;  - main.go - Main orchestration logic&#xA;  - go.mod - Go module with dependencies&#xA;&#xA;  Features Implemented&#xA;&#xA;  ✓ Homepage - Displays bio from content/about.md&#xA;  ✓ Posts listing - Shows all published blog posts at /posts/&#xA;  ✓ Books page - Displays placeholder book data with ratings at /books/&#xA;  ✓ Individual posts - Each post at /blog/{slug}/&#xA;  ✓ Navigation - &amp;quot;Vegard Stikbakke&amp;quot; | &amp;quot;Posts&amp;quot; | &amp;quot;Books&amp;quot; on all pages&#xA;  ✓ Draft filtering - Excludes posts with draft: true (18 published posts)&#xA;  ✓ Slug preservation - Uses slug from frontmatter (falls back to filename)&#xA;  ✓ Clean styling - Minimal, readable CSS&#xA;&#xA;  Usage&#xA;&#xA;  # Build the generator&#xA;  go build -o ssg&#xA;&#xA;  # Generate the site&#xA;  ./ssg&#xA;&#xA;  The site is generated in the public/ directory with this structure:&#xA;  public/&#xA;  ├── index.html                    # Homepage with bio&#xA;  ├── posts/index.html              # Posts listing&#xA;  ├── books/index.html              # Books with placeholder data&#xA;  └── blog/{slug}/index.html        # Individual posts&#xA;&#xA;  The generator successfully processed all 39 markdown files, filtering out 21 drafts and generating 18 published posts plus the placeholder books page.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;I opened the generated HTML files in a browser but was unable to navigate the site correctly, so I asked a follow-up question:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Can you successfully navigate the site?&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ Let me check if the navigation links are properly set up by examining the generated HTML:&#xA;&#xA;⏺ Bash(grep -A 3 &amp;quot;&amp;lt;nav&amp;gt;&amp;quot; public/index.html)&#xA;  ⎿      &amp;lt;nav&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/&amp;quot;&amp;gt;Vegard Stikbakke&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/posts/&amp;quot;&amp;gt;Posts&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/books/&amp;quot;&amp;gt;Books&amp;lt;/a&amp;gt;&#xA;&#xA;⏺ Bash(grep -A 3 &amp;quot;&amp;lt;nav&amp;gt;&amp;quot; public/posts/index.html)&#xA;  ⎿      &amp;lt;nav&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/&amp;quot;&amp;gt;Vegard Stikbakke&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/posts/&amp;quot;&amp;gt;Posts&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/books/&amp;quot;&amp;gt;Books&amp;lt;/a&amp;gt;&#xA;&#xA;⏺ Bash(grep -A 3 &amp;quot;&amp;lt;nav&amp;gt;&amp;quot; public/blog/how-do-pipes-work-sigpipe/index.html)&#xA;  ⎿      &amp;lt;nav&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/&amp;quot;&amp;gt;Vegard Stikbakke&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/posts/&amp;quot;&amp;gt;Posts&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/books/&amp;quot;&amp;gt;Books&amp;lt;/a&amp;gt;&#xA;&#xA;⏺ Bash(grep &#39;href=&amp;quot;/blog/&#39; public/posts/index.html | head -5)&#xA;  ⎿          &amp;lt;a href=&amp;quot;/blog/books-2024/&amp;quot;&amp;gt;Books I read in 2024&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/blog/file-watcher/&amp;quot;&amp;gt;A file watcher&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/blog/extract-table/&amp;quot;&amp;gt;Extract Table API&amp;lt;/a&amp;gt;&#xA;     … +2 lines (ctrl+o to expand)&#xA;&#xA;⏺ Bash(ls public/blog/books-2024/ &amp;amp;&amp;amp; ls public/blog/file-watcher/)&#xA;  ⎿  index.html&#xA;     index.html&#xA;&#xA;⏺ The navigation structure looks correct! However, there&#39;s one important caveat:&#xA;&#xA;  The site uses absolute paths (starting with /), which means:&#xA;&#xA;  ❌ Won&#39;t work if you just open public/index.html directly in a browser (using file:// protocol)&#xA;  ✅ Will work when served by a web server&#xA;&#xA;  You can test it by running a local server:&#xA;&#xA;⏺ Bash(cd public &amp;amp;&amp;amp; python3 -m http.server 8000 &amp;amp;)&#xA;  ⎿  Running in the background (↓ to manage)&#xA;&#xA;⏺ Task Output(non-blocking) b69f192&#xA;  ⎿  (No content)&#xA;&#xA;⏺ Bash(sleep 1 &amp;amp;&amp;amp; curl -s http://localhost:8000/ | grep -A 4 &amp;quot;&amp;lt;nav&amp;gt;&amp;quot;)&#xA;  ⎿      &amp;lt;nav&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/&amp;quot;&amp;gt;Vegard Stikbakke&amp;lt;/a&amp;gt;&#xA;             &amp;lt;a href=&amp;quot;/posts/&amp;quot;&amp;gt;Posts&amp;lt;/a&amp;gt;&#xA;     … +2 lines (ctrl+o to expand)&#xA;&#xA;⏺ Bash(curl -s http://localhost:8000/posts/ | grep -o &#39;&amp;lt;h1&amp;gt;.*&amp;lt;/h1&amp;gt;&#39;)&#xA;  ⎿  &amp;lt;h1&amp;gt;Posts&amp;lt;/h1&amp;gt;&#xA;&#xA;⏺ Bash(curl -s http://localhost:8000/blog/how-do-pipes-work-sigpipe/ | grep -o&#xA;      &#39;&amp;lt;h1&amp;gt;.*&amp;lt;/h1&amp;gt;&#39;)&#xA;  ⎿  &amp;lt;h1&amp;gt;How do Unix pipes work?&amp;lt;/h1&amp;gt;&#xA;&#xA;⏺ Bash(curl -s http://localhost:8000/books/ | grep -o &#39;&amp;lt;h1&amp;gt;.*&amp;lt;/h1&amp;gt;&#39;)&#xA;  ⎿  &amp;lt;h1&amp;gt;Books&amp;lt;/h1&amp;gt;&#xA;&#xA;⏺ Bash(curl -s http://localhost:8000/posts/ | grep -o &#39;href=&amp;quot;/blog/[^&amp;quot;]*&amp;quot;&#39; |&#xA;      head -3)&#xA;  ⎿  href=&amp;quot;/blog/books-2024/&amp;quot;&#xA;     href=&amp;quot;/blog/file-watcher/&amp;quot;&#xA;     href=&amp;quot;/blog/extract-table/&amp;quot;&#xA;&#xA;⏺ Bash(curl -s http://localhost:8000/blog/books-2024/ | grep &#39;&amp;lt;h1&amp;gt;&#39;)&#xA;  ⎿  &amp;lt;h1&amp;gt;Books I read in 2024&amp;lt;/h1&amp;gt;&#xA;&#xA;⏺ Kill Shell(Kill shell: b69f192)&#xA;  ⎿  Shell b69f192 killed&#xA;  ⎿  Interrupted · What should Claude do instead?&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;How to run the local server was useful information, so I told it to add this to a README.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Actually, can you add this to the README? how to serve the files locally&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The README didn&#39;t exist, so it wrote that.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ Write(README.md)&#xA;  ⎿  Wrote 74 lines to README.md&#xA;     # Static Site Generator&#xA;     A minimal static site generator written in Go that converts markdown&#xA;     blog posts to HTML.&#xA;     ## Features&#xA;     - Converts markdown posts to HTML with clean URLs&#xA;     - Preserves slugs from frontmatter&#xA;     - Filters out draft posts&#xA;     - Minimal, readable styling&#xA;     … +64 lines (ctrl+o to expand)&#xA;&#xA;⏺ Done! I&#39;ve created a README.md with clear instructions on how to serve the files locally.&#xA;&#xA;  Yes, the site navigation works perfectly! I tested it by:&#xA;  1. Starting a local Python web server on port 8000&#xA;  2. Accessing all pages (homepage, posts, books, individual posts)&#xA;  3. Verifying all navigation links work correctly&#xA;&#xA;  The site has full navigation between:&#xA;  - Homepage (/) → Bio from about.md&#xA;  - Posts (/posts/) → Listing of 18 published posts&#xA;  - Books (/books/) → Placeholder book data with ratings&#xA;  - Individual posts (/blog/{slug}/) → Each post page&#xA;&#xA;  All pages have the same navigation header: &amp;quot;Vegard Stikbakke&amp;quot; | &amp;quot;Posts&amp;quot; | &amp;quot;Books&amp;quot;&#xA;&#xA;  The only caveat is that the site must be served via a web server (like the Python command in the README) because it uses absolute paths. It won&#39;t work if you just double-click the HTML files to open them directly in a browser.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;If you&#39;re interested, you can see the full session &lt;a href=&#34;/prompts-for-new-site/initial.txt&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;adding-more-functionality&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#adding-more-functionality&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Adding more functionality&lt;/h2&gt;&#xA;&lt;p&gt;One really useful capability for the agent to have is being able to browse websites in a headless browser,&#xA;so I installed Puppeteer as described &lt;a href=&#34;https://github.com/jaenster/puppeteer-mcp-claude&#34;&gt;here&lt;/a&gt;.&#xA;In one session I asked it to tweak the styling:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;I want the blog post page to look more like https://macwright.com/writing.&#xA;It should have two columns: Blog post title and date. The link to the blog&#xA;post should be regular text color but when hovered should have an underline.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;After writing code, it confirmed it worked by using Puppeteer:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;⏺ Excellent! The build succeeded. Let me serve it and take a screenshot to verify the new layout.&#xA;&#xA;⏺ Bash(cd public &amp;amp;&amp;amp; python3 -m http.server 8000 &amp;amp;)&#xA;  ⎿  Running in the background (↓ to manage)&#xA;&#xA;⏺ puppeteer - puppeteer_navigate (MCP)(url: &amp;quot;http://localhost:8000/blog/&amp;quot;)&#xA;  ⎿  Navigated to http://localhost:8000/blog/&#xA;&#xA;⏺ puppeteer - puppeteer_screenshot (MCP)(name: &amp;quot;posts-page&amp;quot;, width: 1200,&#xA;                                        height: 800)&#xA;  ⎿  Screenshot &#39;posts-page&#39; taken at 1200x800&#xA;  ⎿  [Image]&#xA;&#xA;⏺ Great! The layout is working. Let me test the hover effect to verify the underline appears:&#xA;&#xA;⏺ puppeteer - puppeteer_hover (MCP)(selector: &amp;quot;.post-title a&amp;quot;)&#xA;  ⎿  Hovered .post-title a&#xA;&#xA;⏺ puppeteer - puppeteer_screenshot (MCP)(name: &amp;quot;posts-page-hover&amp;quot;, width: 1200,&#xA;                                        height: 800)&#xA;  ⎿  Screenshot &#39;posts-page-hover&#39; taken at 1200x800&#xA;  ⎿  [Image]&#xA;&#xA;⏺ Perfect! The hover effect is working - you can see the underline on &amp;quot;Books I read in 2024&amp;quot; in the hover screenshot. Let me clean up the background server.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;a href=&#34;/prompts-for-new-site/styling.txt&#34;&gt;Here&#39;s the full session&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Here are links to other session you may find interesting:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;/prompts-for-new-site/draft.txt&#34;&gt;Adding previews for draft posts when built locally&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;/prompts-for-new-site/dark-mode.txt&#34;&gt;Adding system aware light/dark mode&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;/prompts-for-new-site/collapsible-code-block.txt&#34;&gt;Collapsing long code blocks&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Happy prompting!&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://x.com/karpathy/status/2004607146781278521?s=20&#34;&gt;Andrej Karpathy&#39;s tweet Dec 26&lt;/a&gt;, &lt;a href=&#34;https://lucumr.pocoo.org/2025/12/22/a-year-of-vibes/&#34;&gt;Armin Ronacher: A Year of Vibes&lt;/a&gt;, &lt;a href=&#34;https://mariozechner.at/posts/2025-11-30-pi-coding-agent/&#34;&gt;Mario Zechner: What I learned building an opinionated and minimal coding agent&lt;/a&gt;, &lt;a href=&#34;https://steipete.me/posts/2025/shipping-at-inference-speed&#34;&gt;Peter Steinberger: Shipping at Inference-Speed&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;</description>
    </item>
    <item>
      <title>Books I read in 2025</title>
      <link>https://vegardstikbakke.com/books-2025/</link>
      <pubDate>Fri, 26 Dec 2025 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/books-2025/</guid>
      <description>&lt;p&gt;I read 16 books this year.&#xA;The full list is at the bottom, and &lt;a href=&#34;https://www.goodreads.com/readingchallenges/gr/annual/2025/QTJQQ0hYVEpMVUZBNVoMjAyNQ&#34;&gt;on Goodreads&lt;/a&gt;.&#xA;These are the books I recommend. Mostly nonfiction, one fiction.&lt;/p&gt;&#xA;&lt;h3 id=&#34;breakneck-chinas-quest-to-engineer-the-future-by-dan-wang&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#breakneck-chinas-quest-to-engineer-the-future-by-dan-wang&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Breakneck: China&#39;s Quest to Engineer the Future by Dan Wang&lt;/h3&gt;&#xA;&lt;p&gt;Dan Wang is a China expert whose book came out this year.&lt;/p&gt;&#xA;&lt;p&gt;The main idea in &lt;em&gt;Breakneck&lt;/em&gt; is that the US is a lawyerly society while China is an engineering state, and at the same time the two peoples are similar in temperament.&#xA;Each chapter focuses on a specific domain - high tech, the one-child policy, Zero COVID.&#xA;China applies engineering to social problems as well.&#xA;The one child policy chapter was particularly harrowing.&lt;/p&gt;&#xA;&lt;p&gt;It&#39;s mind boggling how much rail is built, how many highrises.&#xA;A main takeaway for me is that building big public projects produces civic pride.&lt;/p&gt;&#xA;&lt;p&gt;A fast and engaging read, highly recommended!&#xA;And if you need more, Tyler Cowen&#39;s blurb on the book says&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;The best recent book on China, on China and America, and, arguably, the best book of the year flat out.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h3 id=&#34;all-the-pretty-horses-by-cormac-mccarthy&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#all-the-pretty-horses-by-cormac-mccarthy&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;All the Pretty Horses by Cormac McCarthy&lt;/h3&gt;&#xA;&lt;p&gt;I love Cormac McCarthy!&#xA;His writing style is minimal and with incredible depictions of landscapes and personalities.&#xA;This was the book that launched him into the mainstream.&#xA;You should also &lt;a href=&#34;/books-2024&#34;&gt;read Blood Meridian&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;It&#39;s set in the late 1940s and follows a pair of teenage boys who go to Mexico to work as cowboys.&#xA;An incredible and tragic coming of age book about love, fairness, and the end of the old West.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-scaling-era-an-oral-history-of-ai-2019-2025-by-dwarkesh-patel-gavin-leech&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#the-scaling-era-an-oral-history-of-ai-2019-2025-by-dwarkesh-patel-gavin-leech&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;The Scaling Era: An Oral History of AI, 2019-2025 by Dwarkesh Patel, Gavin Leech&lt;/h3&gt;&#xA;&lt;p&gt;Dwarkesh, still in his early twenties, came out of nowhere to create &lt;a href=&#34;https://www.dwarkesh.com/&#34;&gt;an excellent podcast&lt;/a&gt; that I really enjoy.&#xA;He does hour-long interviews with public intellectuals, executives, academics, engineers.&#xA;He&#39;s done many interviews focused on AI, and this book is a compilation of what leaders in the field think.&lt;/p&gt;&#xA;&lt;p&gt;It&#39;s a great read if you&#39;re familiar with recent AI developments.&#xA;It&#39;s surprisingly well-edited and fun to read.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-soul-of-a-new-machine-by-tracy-kidder&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#the-soul-of-a-new-machine-by-tracy-kidder&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;The Soul of a New Machine by Tracy Kidder&lt;/h3&gt;&#xA;&lt;p&gt;Bryan Cantrill&#39;s &lt;a href=&#34;https://bcantrill.dtrace.org/2019/02/10/reflecting-on-the-soul-of-a-new-machine/&#34;&gt;favorite book&lt;/a&gt; and won a Pulitzer Prize, do you need more convincing?&lt;/p&gt;&#xA;&lt;p&gt;A book about the male urge to build something great.&#xA;It follows Data General Corporation&#39;s skunkworks project as they design (and debug!) a new computer in the 1980s.&#xA;The team is led by the enigmatic Tom West. He hires mostly college graduates, who of course end up working night and day for years.&lt;/p&gt;&#xA;&lt;p&gt;I &lt;a href=&#34;/books-2019&#34;&gt;read this in 2019&lt;/a&gt; but enjoyed it even more the second time round.&#xA;I recommend it to any engineer who would recognize themselves in it.&lt;/p&gt;&#xA;&lt;h3 id=&#34;show-stopper-the-breakneck-race-to-create-windows-nt-and-the-next-generation-at-microsoft-by-g-pascal-zachary&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#show-stopper-the-breakneck-race-to-create-windows-nt-and-the-next-generation-at-microsoft-by-g-pascal-zachary&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Show Stopper!: The Breakneck Race to Create Windows NT and the Next Generation at Microsoft by G. Pascal Zachary&lt;/h3&gt;&#xA;&lt;p&gt;Like &lt;em&gt;The Soul of a New Machine&lt;/em&gt;, this follows a big technology project: The building and shipping of Windows NT at Microsoft.&lt;/p&gt;&#xA;&lt;p&gt;As project leader Dave Cutler says, the creation of Windows NT may be the last time anyone ever assembles a team to build a completely new computer operating system, and this book gives a good account of the personalities, the stresses, and the working environment involved in making it happen.&lt;/p&gt;&#xA;&lt;p&gt;Recommended, but don&#39;t pick this over &lt;em&gt;The Soul of a New Machine&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;hackers-heroes-of-the-computer-revolution-by-steven-levy&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#hackers-heroes-of-the-computer-revolution-by-steven-levy&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Hackers: Heroes of the Computer Revolution by Steven Levy&lt;/h3&gt;&#xA;&lt;p&gt;A collection of biographies of groups of hackers from the 1950s through the 1980s.&#xA;Mainframe hackers, hardware hackers, game hackers.&lt;/p&gt;&#xA;&lt;p&gt;I enjoyed this a lot.&#xA;Although at almost 500 pages you need to be interested in this stuff.&lt;/p&gt;&#xA;&lt;h3 id=&#34;steve-jobs--the-next-big-thing-by-randall-e-stross&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#steve-jobs--the-next-big-thing-by-randall-e-stross&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Steve Jobs &amp;amp; The NeXT Big Thing by Randall E. Stross&lt;/h3&gt;&#xA;&lt;p&gt;10 years after starting apple and having started the personal computer revolution, Jobs resigned after a clash of visions with the new CEO.&#xA;This is when he starts NeXT.&#xA;NeXT flops commercially, but is bought by Apple after 13 years.&#xA;Jobs, having now returned to Apple, has an incredible second stint at Apple.&#xA;This is of course when they invented the iPod and iPhone.&lt;/p&gt;&#xA;&lt;p&gt;Crucially, this book is written before Jobs&#39; final arc.&#xA;This book is just about NeXT and how thoroughly Jobs messed up, and it&#39;s very interesting to read.&#xA;A lot of hubris, overengineering, and failing to consider the tradeoffs necessary to produce a good business.&#xA;It&#39;s a very different book than the biography of Jobs by Walter Isaacson, which is a lot more favorable to Jobs.&lt;/p&gt;&#xA;&lt;p&gt;The operating system developed at NeXT, NeXTSTEP, became the foundation of what&#39;s now macOS and iOS, so today we have a lot to thank them for!&lt;/p&gt;&#xA;&lt;h2 id=&#34;full-list&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#full-list&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Full list&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;All the Pretty Horses&lt;/strong&gt; (1992) by Cormac McCarthy&lt;/li&gt;&#xA;&lt;li&gt;Writing for Developers: Blogs that get read (2025) by Piotr Sarna, Cynthia Dunlop&lt;/li&gt;&#xA;&lt;li&gt;The Crossing (1994) by Cormac McCarthy&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Soul of a New Machine&lt;/strong&gt; (1981) by Tracy Kidder (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Show Stopper!: The Breakneck Race to Create Windows NT and the Next Generation at Microsoft&lt;/strong&gt; (1994) by G. Pascal Zachary (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;A Philosophy of Software Design (2019) by John Osterhout (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Sandworm: A New Era of Cyberwar and the Hunt for the Kremlin&#39;s Most Dangerous Hackers (2019) by Andy Greenberg&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Steve Jobs &amp;amp; The NeXT Big Thing&lt;/strong&gt; (1993) by Randall E. Stross&lt;/li&gt;&#xA;&lt;li&gt;Conspiracy: Peter Thiel, Hulk Hogan, Gawker, and the Anatomy of Intrigue (2018) by Ryan Holiday&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Hackers: Heroes of the Computer Revolution&lt;/strong&gt; (1984) by Steven Levy&lt;/li&gt;&#xA;&lt;li&gt;The Staff Engineer&#39;s Path: A Guide for Individual Contributors Navigating Growth and Change (2022) by Tanya O. Reilly&lt;/li&gt;&#xA;&lt;li&gt;UNIX: A History and a Memoir (2019) by Brian W. Kernighan (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (2010) by Dustin Boswell (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Breakneck: China&#39;s Quest to Engineer the Future&lt;/strong&gt; (2025) by Dan Wang&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Scaling Era: An Oral History of AI, 2019-2025&lt;/strong&gt; (2025) by Dwarkesh Patel, Gavin Leech&lt;/li&gt;&#xA;&lt;li&gt;Too Big to Fail (2009) by Andrew Ross Sorkin&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Books I read in 2024</title>
      <link>https://vegardstikbakke.com/books-2024/</link>
      <pubDate>Sun, 12 Jan 2025 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/books-2024/</guid>
      <description>&lt;p&gt;I finished 9 books this year, in total &lt;a href=&#34;https://www.goodreads.com/user/year_in_books/2024/3400170&#34;&gt;2006 pages&lt;/a&gt;. That is on the lower end of reading since I started tracking, not that anyone&#39;s counting!&#xA;&lt;code&gt;▁▃█▅▃▄▃▁&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;As usual, recommendations in bold. Below I&#39;ll briefly say something about each book.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Tidy First? A Personal Exercise in Empirical Software Design&lt;/strong&gt; (2024) by Kent Beck&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Death of Ivan Ilyich&lt;/strong&gt; (1886) by Leo Tolstoy&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Blood Meridian&lt;/strong&gt; (1985) by Cormac McCarthy (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;The Spy Who Came In From the Cold (1963) by John le Carré&lt;/li&gt;&#xA;&lt;li&gt;As I Lay Dying (1930) by William Faulkner&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Undoing Project: A Friendship That Changed Our Minds&lt;/strong&gt; (2016) by Michael Lewis&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Old Man and the Sea&lt;/strong&gt; (1952) by Ernest Hemingway&lt;/li&gt;&#xA;&lt;li&gt;Code Health Guardian: The Old-New Role of a Human Programmer in the AI Era (2024) by Artie Shevchenko&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;21 Church Fathers&lt;/strong&gt; (2000) by Peter Halldorf&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;There were a handful of books I spent a meaningful amount of time reading but didn&#39;t complete.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Making of the Atomic Bomb&lt;/strong&gt; (1986) by Richard Rhodes&lt;/li&gt;&#xA;&lt;li&gt;Softwar: An Intimate Portrait of Larry Ellison and Oracle (2003) by Matthew Symonds&lt;/li&gt;&#xA;&lt;li&gt;Rust in Action (2021) by Tim McNamara&lt;/li&gt;&#xA;&lt;li&gt;Hyperion (1989) by Dan Simmons&lt;/li&gt;&#xA;&lt;li&gt;Writing for Developers: Blogs that Get Read by Piotr Sarna and Cynthia Dunlop (2024)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;fiction&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#fiction&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Fiction&lt;/h3&gt;&#xA;&lt;p&gt;I read more fiction books than non-fiction books this year! 🎉&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The Death of Ivan Ilyich (1886) by Leo Tolstoy. A gripping novella about death, family, career, facades, hypocrisy, and the meaning of life. You should read it!&lt;/li&gt;&#xA;&lt;li&gt;Blood Meridian (1985) by Cormac McCarthy. So beautiful! McCarthy&#39;s prose is just incredible. See &lt;a href=&#34;https://biblioklept.org/2017/09/18/probably-not-all-of-the-similes-in-blood-meridian/&#34;&gt;(Probably not) All of the similes in Blood Meridian&lt;/a&gt;. And so dark! It&#39;s basically about violence and evil. Set in the mid-1800s, follows the Kid who joins a scalp-hunting expedition led by the enigmatic Judge. The Judge is rendered so well that I&#39;m not sure I would watch a movie adaptation if there ever is one.&lt;/li&gt;&#xA;&lt;li&gt;The Spy Who Came In From the Cold (1963) by John le Carré. I wanted to try something by the renowned spy novel master, and was recommended this one. It was okay 🤷‍♂️&lt;/li&gt;&#xA;&lt;li&gt;As I Lay Dying (1930) by William Faulkner. Set in the South. The mother of a family is dying and each chapter is narrated by one of the grieving family members. It was interesting and engaging, and probably a classic for good reason. There&#39;s often talk of Faulkerian writing, but I didn&#39;t see anything super particular in his style. The book hasn&#39;t been much on my mind since I read it, but worth reading anyway.&lt;/li&gt;&#xA;&lt;li&gt;The Old Man and the Sea (1952) by Ernest Hemingway. I read this in two sittings! It&#39;s short, exciting and touching.&lt;/li&gt;&#xA;&lt;li&gt;Hyperion (1989) by Dan Simmons. As with so many science fiction books, I loved the exposition. But the book is too lengthy and too dialogue heavy, so I abandoned it after 200 pages.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;non-fiction&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#non-fiction&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Non-fiction&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Tidy First? A Personal Exercise in Empirical Software Design (2024) by Kent Beck. A small book (~100 pages) by &lt;a href=&#34;https://en.wikipedia.org/wiki/Kent_Beck&#34;&gt;Kent Beck&lt;/a&gt; of agile and TDD fame. Very pragmatic and enjoyable book on how to think about the balance between adding new features to a codebase and improving the code.&lt;/li&gt;&#xA;&lt;li&gt;The Undoing Project: A Friendship That Changed Our Minds (2016) by Michael Lewis. Michael Lewis needs no introduction. This is about Daniel Kahneman and Amos Tversky, the Israeli psychologists who founded behavioral economics. Super engaging as always with Lewis, and interesting subject.&lt;/li&gt;&#xA;&lt;li&gt;Code Health Guardian: The Old-New Role of a Human Programmer in the AI Era (2024) by Artie Shevchenko. Very 2024 book title, but the book is less about AI and more some good recommendations about maintaining a codebase.&lt;/li&gt;&#xA;&lt;li&gt;21 Church Fathers (2000) by Peter Halldorf. Great primer on the most influential people in the Church in the first millennium.&lt;/li&gt;&#xA;&lt;li&gt;The Making of the Atomic Bomb (1986) by Richard Rhodes. At almost 900 pages, this is an undertaking! Rhodes is a historian but also a journalist, which shines through because this is a fun read. It covers the people and discoveries that led to nuclear fission and the atomic bomb. Not surprisingly it won the Pulitzer and is &lt;a href=&#34;https://en.wikipedia.org/wiki/The_Making_of_the_Atomic_Bomb#Reception&#34;&gt;praised in general&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Softwar: An Intimate Portrait of Larry Ellison and Oracle (2003) by Matthew Symonds. Ellison is founder of Oracle and the third-wealthiest person in the world.&#xA;An interesting read! The book is very detailed. It feels like it covers every happening in Oracle since the founding until the publishing of the book.&#xA;Thus I didn&#39;t end up finishing it, only about 250 out of 720 pages in.&#xA;What&#39;s fun about the book is that Ellison required being able to add his own footnotes to it. Symonds had the last word on the text in the book itself, Ellison on the footnoes.&#xA;These - surprisingly to me - made him come across as both humble and sympathetic.&lt;/li&gt;&#xA;&lt;li&gt;Rust in Action (2021) by Tim McNamara. Writing a lot of Rust at work, which is both fun and frustrating. Haven&#39;t used this enough to say anything sensible, though!&lt;/li&gt;&#xA;&lt;li&gt;Writing for Developers: Blogs that Get Read by Piotr Sarna, Cynthia Dunlop (2024). Very practical book about writing blogs. Was released late December, so haven&#39;t finished it yet. If you want to read it, you should join &lt;a href=&#34;https://eatonphil.com/2025-writing-for-developers.html&#34;&gt;Phil Eaton&#39;s book club on it&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;source-code-for-the-graphic-above&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#source-code-for-the-graphic-above&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Source code for the graphic above&lt;/h4&gt;&#xA;&lt;pre&gt;&lt;code&gt;~ # cat book_stats.csv | awk -F &#39;,&#39; &#39;NR&amp;gt;1 {print $3}&#39; | spark&#xA;▁▃█▅▃▄▃▁&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;where &lt;code&gt;spark&lt;/code&gt; is Zach Holman&#39;s https://zachholman.com/spark/.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>A file watcher</title>
      <link>https://vegardstikbakke.com/file-watcher/</link>
      <pubDate>Sun, 25 Aug 2024 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/file-watcher/</guid>
      <description>&lt;p&gt;I wrote &lt;a href=&#34;https://github.com/vegarsti/reload&#34;&gt;&lt;code&gt;reload&lt;/code&gt;&lt;/a&gt;, my own file watcher!&lt;/p&gt;&#xA;&lt;p&gt;The last year and a half I&#39;ve been making my way through &lt;a href=&#34;https://csprimer.com/&#34;&gt;CSPrimer&lt;/a&gt;.&#xA;It consists of hundreds of computer science problems, so working through these means I&#39;m writing hundreds of small programs.&#xA;Like most programming, it&#39;s a very iterative process where I tweak my program and rerun it, typically switching from my editor to run a command.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;gcc main.c -o main &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./main&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It would be nice if this just reran automatically whenever I needed it to, so I didn&#39;t have to switch between my editor and the terminal!&lt;/p&gt;&#xA;&lt;p&gt;Of course, there exist tools like this. They&#39;re sometimes called file watchers or hot reloaders.&#xA;I tried a few existing tools such as &lt;a href=&#34;https://github.com/eradman/entr&#34;&gt;entr&lt;/a&gt; and &lt;a href=&#34;https://github.com/quackingduck/wach&#34;&gt;wach&lt;/a&gt; but they weren&#39;t &lt;em&gt;just right&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;So I figured I&#39;d write my own.&lt;/p&gt;&#xA;&lt;p&gt;After a few hours the result was &lt;a href=&#34;https://github.com/vegarsti/reload&#34;&gt;&lt;code&gt;reload&lt;/code&gt;&lt;/a&gt;. It&#39;s one &lt;code&gt;main.go&lt;/code&gt; file with less than 200 lines. It does exactly what I need, nothing more.&lt;/p&gt;&#xA;&lt;h3 id=&#34;requirements&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#requirements&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Requirements&lt;/h3&gt;&#xA;&lt;p&gt;I wanted to be able to just put &lt;code&gt;reload&lt;/code&gt; in front of the command I&#39;d usually run.&#xA;To do that, what &lt;code&gt;reload&lt;/code&gt; needed to do was&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;figure out which file(s) to watch&lt;/li&gt;&#xA;&lt;li&gt;figure out which command to run&lt;/li&gt;&#xA;&lt;li&gt;run the command&lt;/li&gt;&#xA;&lt;li&gt;listen to file changes&lt;/li&gt;&#xA;&lt;li&gt;reload the command on file changes&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;And it also needed to&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;run command pipelines like &lt;code&gt;gcc main.c -o main &amp;amp;&amp;amp; ./main&lt;/code&gt; sequentially&lt;/li&gt;&#xA;&lt;li&gt;cancel the command if it&#39;s still running when there&#39;s a file change&lt;/li&gt;&#xA;&lt;li&gt;deduplicate file events -- so that a change to &lt;code&gt;main.c&lt;/code&gt; and the &lt;code&gt;main&lt;/code&gt; binary file don&#39;t trigger two subsequent reloads&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;There needs to be some concurrent code here, but Go excels at this with channels and goroutines.&lt;/p&gt;&#xA;&lt;p&gt;In the rest of the post, let&#39;s look at how &lt;code&gt;reload&lt;/code&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Figures out which files to watch&lt;/li&gt;&#xA;&lt;li&gt;Detects file changes&lt;/li&gt;&#xA;&lt;li&gt;Runs the command&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;figuring-out-which-files-to-watch&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#figuring-out-which-files-to-watch&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Figuring out which files to watch&lt;/h3&gt;&#xA;&lt;p&gt;Some of these tools have you explicitly list the files that should trigger a reload.&#xA;For my use case, my command usually has the filename already.&#xA;Why not just grab that?&lt;/p&gt;&#xA;&lt;p&gt;So &lt;code&gt;reload&lt;/code&gt; looks at each part of the command, and checks if it&#39;s the filename of an existing file.&#xA;If it is, we add it to the list of files to watch.&#xA;If we didn&#39;t find any file, we fall back to watching the working directory.&lt;/p&gt;&#xA;&lt;p&gt;Kinda neat!&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Find files in command that we will watch&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toWatch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;part&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Check if there&amp;#39;s a file to watch&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Stat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;part&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;IsNotExist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nf&#34;&gt;check&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;slices&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toWatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;toWatch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toWatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Fall back to watching the working directory&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toWatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;wd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Getwd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nf&#34;&gt;check&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;toWatch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toWatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;wd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;detecting-file-changes&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#detecting-file-changes&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Detecting file changes&lt;/h3&gt;&#xA;&lt;p&gt;The most unfamiliar thing to me here was watching for file changes.&lt;/p&gt;&#xA;&lt;p&gt;I used the &lt;a href=&#34;https://github.com/fsnotify/fsnotify&#34;&gt;fsnotify&lt;/a&gt; package to handle file notifications.&#xA;It supports all the usual platforms like macOS, Windows, and Linux.&#xA;On macOS it uses the &lt;a href=&#34;https://en.wikipedia.org/wiki/Kqueue&#34;&gt;kqueue&lt;/a&gt; notification interface, whereas on Linux it uses &lt;a href=&#34;https://en.wikipedia.org/wiki/Inotify&#34;&gt;inotify&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Here&#39;s the code for that.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fsnotify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewWatcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;check&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;toWatch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nf&#34;&gt;check&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;fileChanges&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;chan&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;wg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Done&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;lastChange&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;dedupWindow&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Millisecond&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;watcher&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Events&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Has&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fsnotify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;c1&#34;&gt;// Treat multiple events at same time as one&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Since&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastChange&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dedupWindow&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;lastChange&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;fileChanges&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And we listen on the &lt;code&gt;fileChanges&lt;/code&gt; channel in the code to run the command, which we will see now.&lt;/p&gt;&#xA;&lt;h3 id=&#34;running-commands&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#running-commands&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Running commands&lt;/h3&gt;&#xA;&lt;p&gt;To create a command that we can run, and cancel, we use &lt;a href=&#34;https://pkg.go.dev/os/exec#CommandContext&#34;&gt;&lt;code&gt;exec.CommandContext&lt;/code&gt;&lt;/a&gt; from Go&#39;s standard library.&#xA;We can specify its standard in, standard out, and standard error.&#xA;In our case, we want standard out and standard error to be the operating system&#39;s stdout and stderr, since we want the command to behave as if we ran it directly in the terminal.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://man7.org/linux/man-pages/man1/sh.1p.html&#34;&gt;&lt;code&gt;sh -c &#39;&amp;lt;command&amp;gt;&#39;&lt;/code&gt;&lt;/a&gt; allows us to pass the full provided command. All use of pipes and operators like &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; will just work, as it&#39;s handed over to the shell.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// In `main()`&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// First run the command&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;runCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fileChanges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Then rerun it on file changes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fileChanges&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;--- Changed: %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;--- Running: %s\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nf&#34;&gt;runCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fileChanges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;runCommand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fileChanges&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;chan&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Create child context so we can cancel this command&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// without cancelling the entire program&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;commandCtx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;commandCancel&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WithCancel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;commandCancel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Cancel and rerun the command if the file changes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fileChanges&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;c1&#34;&gt;// The channel was closed, shut down&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nf&#34;&gt;commandCancel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;nx&#34;&gt;fileChanges&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// Run the command using `sh -c &amp;lt;command&amp;gt;` to allow for&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;c1&#34;&gt;// shell syntax such as pipes and boolean operators&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;exec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;CommandContext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;commandCtx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;sh&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;-c&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// It&amp;#39;s fine if the command fails!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I hope you&#39;ll try it out!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Extract Table API</title>
      <link>https://vegardstikbakke.com/extract-table/</link>
      <pubDate>Sat, 17 Aug 2024 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/extract-table/</guid>
      <description>&lt;p&gt;How great would it be to pull a table from an image or PDF and instantly turn it into structured text?&#xA;&lt;img src=&#34;https://results.extract-table.com/showcase.png&#34; alt=&#34;The table&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;I built this! Check out &lt;a href=&#34;https://extract-table.com&#34;&gt;extract-table.com&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://results.extract-table.com/screenshot.png&#34; alt=&#34;The table&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Uploading an image produces a result like in the screenshot above, hosted at &lt;a href=&#34;https://results.extract-table.com/52bc7d95f07a30eef023e36bde0de9c3aab7e057aaa854b44419291ec56b0839&#34;&gt;result.extract-table.com&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Please try it!&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;The site uses &lt;code&gt;api.extract-table.com&lt;/code&gt; which can also be used directly.&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;$ curl https://api.extract-table.com -X \&#xA;    POST -H &amp;quot;Content-Type: image/png&amp;quot; \&#xA;    -H &amp;quot;Accept: text/csv&amp;quot; \&#xA;    --data-binary @invoice.png&#xA;Items,Type,,Quantity,Price,Total&#xA;Test lonut,Rate,Plan,2,$11.00/$242.00,$253.00&#xA;bulk import item2,MRC,,1,$2.20,$2.20&#xA;bulk import item2 desc,,,,,&#xA;bulk import item4,NRC,,2,$4.40,$8.80&#xA;bulk import item4 desc,,,,,&#xA;bulk import item7,NRC,,2,$0.00,$0.00&#xA;bulk import item7 desc,,,,,&#xA;asdasd,NRC,,2,$0.00,$0.00&#xA;asdasd,,,,,&#xA;mine,MRC,,2,$121.00,$242.00&#xA;2bulk import item1,MRC,,1,$33.00,$33.00&#xA;bulk import item 1 desc,,,,,&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The API is an AWS Lambda function which uses &lt;a href=&#34;https://aws.amazon.com/textract/&#34;&gt;Amazon Textract&lt;/a&gt;, AWS&#39;s OCR service that gives you all words with their bounding box.&#xA;It even has a mode for detecting tables, which is 10x more expensive.&lt;/p&gt;&#xA;&lt;p&gt;I actually built this in 2021 and &lt;a href=&#34;https://news.ycombinator.com/item?id=28680136&#34;&gt;posted about it on Hacker News&lt;/a&gt;.&#xA;Since then the API has been used 26 thousand times.&#xA;I originally used the table detection algorithm, but that became too expensive to host for free.&lt;/p&gt;&#xA;&lt;p&gt;I&#39;ve since written my own table detection algorithm, which was used to produce the result above.&#xA;But explaining that algorithm is for another post!&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/vegarsti/extract-table&#34;&gt;Browse the code&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Blogs I like</title>
      <link>https://vegardstikbakke.com/blogroll/</link>
      <pubDate>Wed, 31 Jul 2024 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/blogroll/</guid>
      <description>&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://mht.wtf/&#34;&gt;Martin Hafskjold Thoresen&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://healeycodes.com/&#34;&gt;Andrew Healey&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://benhoyt.com/writings/&#34;&gt;Ben Hoyt&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://notes.eatonphil.com/&#34;&gt;Phil Eaton&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://justinjaffray.com/&#34;&gt;Justin Jaffray&lt;/a&gt; (&lt;a href=&#34;https://buttondown.com/jaffray&#34;&gt;newsletter&lt;/a&gt;)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://jvns.ca&#34;&gt;Julia Evans&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://danluu.com&#34;&gt;Dan Luu&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://erikbern.com&#34;&gt;Erik Bernhardsson&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://routley.io/&#34;&gt;James Routley&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.benkuhn.net&#34;&gt;Ben Kuhn&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.evanmiller.org&#34;&gt;Evan Miller&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://www.paulgraham.com/articles.html&#34;&gt;Paul Graham&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://steve-yegge.blogspot.com&#34;&gt;Steve Yegge 1&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://sites.google.com/site/steveyegge2/blog-rants&#34;&gt;Steve Yegge 2&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://ballingt.com&#34;&gt;Thomas Ballinger&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://eli.thegreenplace.net&#34;&gt;Eli Bendersky&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://sirupsen.com/about&#34;&gt;Simon Eskildsen&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://bcantrill.dtrace.org/&#34;&gt;Bryan Cantrill&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://ahl.dtrace.org/blog/&#34;&gt;Adam Leventhal&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.seangoedecke.com/&#34;&gt;Sean Goedecke&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://dubroy.com/blog/&#34;&gt;Patrick Dubroy&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Books I read in 2023</title>
      <link>https://vegardstikbakke.com/books-2023/</link>
      <pubDate>Sat, 06 Jan 2024 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/books-2023/</guid>
      <description>&lt;p&gt;I read 11 books this year, in total &lt;a href=&#34;https://www.goodreads.com/user/year_in_books/2023/3400170&#34;&gt;4488 pages&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I would recommend all of the books in bold.&#xA;That turned out to be almost all of them.&#xA;Below the list I&#39;ll group the books and talk a bit about each book.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;How the World Really Works: A Scientist&#39;s Guide to Our Past, Present and Future&lt;/strong&gt; (2022) by Vaclav Smil&lt;/li&gt;&#xA;&lt;li&gt;A Promised Land (2020) by Barack Obama&lt;/li&gt;&#xA;&lt;li&gt;Working in Public: The Making and Maintenance of Open Source Software (2020) by Nadia Eghbal&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Crafting Interpreters&lt;/strong&gt; (2021) by Robert Nystrom&lt;/li&gt;&#xA;&lt;li&gt;Skunk Works: A Personal Memoir of My Years at Lockheed (1994) by Ben R. Rich &amp;amp; Leo Janos&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Idea Factory: Bell Labs and the Great Age of American Innovation&lt;/strong&gt; (2012) by Jon Gertner&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Dealers of Lightning: Xerox PARC and the Dawn of the Computer Age&lt;/strong&gt; (2000) by Michael A. Hiltzik&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A Mind At Play&lt;/strong&gt; (2017) by Jimmy Soni &amp;amp; Rob Goodman&lt;/li&gt;&#xA;&lt;li&gt;Of Mice and Men (1937) by John Steinbeck&lt;/li&gt;&#xA;&lt;li&gt;American Pastoral (1997) by Philip Roth&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Poor Charlie&#39;s Almanack: The Essential Wit and Wisdom of Charles T. Munger&lt;/strong&gt; (2005) by Charles T. Munger&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;history-of-technology&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#history-of-technology&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;History of technology&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Skunk Works: A Personal Memoir of My Years at Lockheed (1994) by Ben R. Rich &amp;amp; Leo Janos.&#xA;This was sadly underwhelming, I ended up slogging through it.&#xA;It has a really high ranking on Goodreads (4.4), and several people have said it&#39;s one of their favorite books.&#xA;I found it to be long winded and lacking a good narrative throughout.&#xA;It would have benefited from better editing.&#xA;Ben Rich was the director of Lockheed from 1975 to 1991, and Lockheed developed stealth airplanes that were crucial in the Cold War.&#xA;So it was still interesting to learn about Lockheed and the stealth airplanes they developed.&#xA;Crucially they did this with a capable and nimble team unimpeded by bureacracy, and I think this is what makes it popular.&#xA;A lot of this thinking has clearly influenced a lot of tech companies.&lt;/li&gt;&#xA;&lt;li&gt;The Idea Factory: Bell Labs and the Great Age of American Innovation (2012) by Jon Gertner.&#xA;This is a must read if you&#39;re into the history of tech!&#xA;Super interesting, very well narrated, about the research lab that all big companies dream of emulating.&#xA;Bell Telephone was a monopoly, the only (?) phone company in the US, which made a lot of money.&#xA;Luckily they used a lot of the profits to fund basic research at Bell Labs.&#xA;So much of the technology that the modern world relies on was invented at Bell Labs.&#xA;To list some out of &lt;a href=&#34;https://ethw.org/Bell_Telephone_Laboratories,_Inc._List_of_Significant_Innovations_%26_Discoveries_(1925-1983)&#34;&gt;a long list&lt;/a&gt;:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The transistor&lt;/li&gt;&#xA;&lt;li&gt;Laser&lt;/li&gt;&#xA;&lt;li&gt;Quality control theory&lt;/li&gt;&#xA;&lt;li&gt;Information theory&lt;/li&gt;&#xA;&lt;li&gt;Computer music (in 1957!)&lt;/li&gt;&#xA;&lt;li&gt;Computer art (1962!)&lt;/li&gt;&#xA;&lt;li&gt;The fast Fourier transform (FFT)&lt;/li&gt;&#xA;&lt;li&gt;Unix&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Dealers of Lightning: Xerox PARC and the Dawn of the Computer Age (2000) by Michael A. Hiltzik.&#xA;Not as gripping as The Idea Factory, but still enjoyable.&#xA;Xerox failed to commercialize their innovations, but PARC invented so many things we take for granted in computers, and many alumni went on to be crucial at Apple and Microsoft.&#xA;&lt;ul&gt;&#xA;&lt;li&gt;the graphical user interface (GUI)&lt;/li&gt;&#xA;&lt;li&gt;WYSIWYG text editing&lt;/li&gt;&#xA;&lt;li&gt;object-oriented programming (OOP)&lt;/li&gt;&#xA;&lt;li&gt;Ethernet&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;programming&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#programming&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Programming&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Crafting Interpreters (2021) by Robert Nystrom.&#xA;Highly recommended if you&#39;re a programmer!&#xA;It walks through implementing a programming language using two different approaches and languages.&#xA;Good prose with fun illustrations and analogies, which makes it extra fun to read.&#xA;I followed along by typing in all the code.&#xA;This is a long book (865 pages) so that was time consuming, but really educational.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;biographies&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#biographies&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Biographies&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A Promised Land (2020) by Barack Obama.&#xA;I honestly don&#39;t remember much of this.&#xA;It covers his campaign and his first term as President.&lt;/li&gt;&#xA;&lt;li&gt;A Mind At Play (2017) by Jimmy Soni &amp;amp; Rob Goodman.&#xA;Great read.&#xA;Biography about Claude Shannon, the mathematician who came up with information theory while at Bell Labs.&#xA;Brilliant and very strange guy.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;other-non-fiction&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#other-non-fiction&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Other non fiction&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How the World Really Works: A Scientist&#39;s Guide to Our Past, Present and Future (2022) by Vaclav Smil.&#xA;You should really read this if you want to know about climate change and what is actually required to transition away from carbons.&#xA;It&#39;s very dense, but very interesting, and just ~250 pages.&#xA;Chock full of numbers and sentences like &lt;em&gt;&amp;quot;In two centuries, the human labor to produce a kilogram of American wheat was reduced from 10 minutes to less than two seconds.&amp;quot;&lt;/em&gt;&#xA;Smil has a holistic systems thinking view of the world which seems obviously correct.&#xA;Again, read this.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;Reading Vaclav Smil&amp;#39;s new book How the World Really Works. Great book. Very effective at conveying how thoroughly dependent we are on fossil fuels.&lt;/p&gt;&amp;mdash; Vegard Stikbakke (@vegardstikbakke) &lt;a href=&#34;https://twitter.com/vegardstikbakke/status/1621766090010370049?ref_src=twsrc%5Etfw&#34;&gt;February 4, 2023&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Working in Public: The Making and Maintenance of Open Source Software (2020) by Nadia Eghbal.&#xA;Recommended reading if you&#39;re a programmer.&#xA;About open source and the various models of open source projects that exist.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Poor Charlie&#39;s Almanack: The Essential Wit and Wisdom of Charles T. Munger (2005) by Charles T. Munger.&#xA;This was just re-released by Stripe Press.&#xA;They keep publishing bangers.&#xA;Essentially a collection of speeches by Charlie Munger, the lesser known partner of Warren Buffett.&#xA;Munger is well known in certain circles for his focus on mental models.&#xA;He has a lot of interesting takes on &amp;quot;the psychology of misjudgment&amp;quot;, essentially biases, which he claims he&#39;s used to good effect over the years.&#xA;And &lt;a href=&#34;https://finance.yahoo.com/quote/BRK-A/?guccounter=1&amp;amp;guce_referrer=aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8&amp;amp;guce_referrer_sig=AQAAACk383H2w19DiyHdSO1a9luiM42uFjWBD-q4llNEIGvgZre2YkviWXE6j7n8zfEYZDGgob2knfd1TWnGzgCsjv-iLq3rN4K6tH9WA-o_wy2zNe9xGa2f2q5fowxjgeaFqJmrxFXyWMnvJkr7cIgnaCYHTWid8cuwhbEOCgvyac-b&#34;&gt;his (Berkshire Hathaway&#39;s) record speaks for itself&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;&amp;quot;If the facts don&#39;t hang together on a latticework of theory, you don&#39;t have them in a usable form. You&#39;ve got to have mental models in your head.&amp;quot;&lt;/em&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;fiction&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#fiction&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Fiction&lt;/h3&gt;&#xA;&lt;p&gt;Two books by prominent American novelists.&#xA;I really enjoyed both!&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Of Mice and Men (1937) by John Steinbeck.&#xA;I&#39;ve had this in my bookshelf for a few years.&#xA;Short and heart warming and sad.&lt;/li&gt;&#xA;&lt;li&gt;American Pastoral (1997) by Philip Roth.&#xA;Very funny and interesting. I&#39;d been wanting to read something by Philip Roth, who is regarded as one of the great American writers of the 20th century.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;Just posted my annual roundup of the books I read last year.&lt;br&gt;&lt;br&gt;Some tech history, some biographies, some fiction, and a programming book.&lt;br&gt;&lt;br&gt;Don&amp;#39;t read Skunk Works.&lt;br&gt;&lt;br&gt;Do read Vaclav Smil, you&amp;#39;ll be more informed on climate change. &lt;a href=&#34;https://t.co/6ymMzMSkwV&#34;&gt;pic.twitter.com/6ymMzMSkwV&lt;/a&gt;&lt;/p&gt;&amp;mdash; Vegard Stikbakke (@vegardstikbakke) &lt;a href=&#34;https://twitter.com/vegardstikbakke/status/1743601772277457273?ref_src=twsrc%5Etfw&#34;&gt;January 6, 2024&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;</description>
    </item>
    <item>
      <title>Books I read in 2022</title>
      <link>https://vegardstikbakke.com/books-2022/</link>
      <pubDate>Thu, 12 Jan 2023 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/books-2022/</guid>
      <description>&lt;p&gt;I read 14 books this year, spanning &lt;a href=&#34;https://www.goodreads.com/user/year_in_books/2022/3400170&#34;&gt;5878 pages&lt;/a&gt;.&#xA;Not too bad with two kids of 0 and 2 years.&lt;/p&gt;&#xA;&lt;p&gt;I&#39;m trying something new this year, so instead of just listing out the books I&#39;m also going to group them and say something about them.&#xA;As always, those in bold were my favourites.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Master of the Senate&lt;/strong&gt; (2002) by Robert Caro&lt;/li&gt;&#xA;&lt;li&gt;The Making of Prince of Persia: Journals 1985 - 1993 (2011) Jordan Mechner&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Passage of Power&lt;/strong&gt; (2012) by Robert Caro&lt;/li&gt;&#xA;&lt;li&gt;The Founders: The Story of Paypal (2022) by Jimmy Soni&lt;/li&gt;&#xA;&lt;li&gt;Working Backwards: Insights, Stories, and Secrets from Inside Amazon (2021) by Bryar and Carr&lt;/li&gt;&#xA;&lt;li&gt;The Big Score: The Billion Dollar Story of Silicon Valley (1985) by Michael Malone&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Kotlin in Action&lt;/strong&gt; (2016) by Jemerov and Isakova&lt;/li&gt;&#xA;&lt;li&gt;Moneyball (2003) by Michael Lewis&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Mythical Man Month&lt;/strong&gt; (1975) by Fred Brooks (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;The Big Short (2010) by Michael Lewis&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Last Lion: Winston Spencer Churchill: Visions of Glory, 1874-1932&lt;/strong&gt; (1983) by William Manchester&lt;/li&gt;&#xA;&lt;li&gt;How Query Engines Work (2022) Andy Grove&lt;/li&gt;&#xA;&lt;li&gt;Never Let Me Go (2005) by Kazuo Ishiguro&lt;/li&gt;&#xA;&lt;li&gt;The Premonition: A Pandemic Story (2021) by Michael Lewis&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;biographies&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#biographies&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Biographies&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Master of the Senate (2002) by Robert Caro&lt;/li&gt;&#xA;&lt;li&gt;The Passage of Power (2012) by Robert Caro&lt;/li&gt;&#xA;&lt;li&gt;The Last Lion: Winston Spencer Churchill: Visions of Glory, 1874-1932 (1983) by William Manchester&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;My first and only big reads this year were book 3 and 4 of &lt;a href=&#34;https://en.wikipedia.org/wiki/The_Years_of_Lyndon_Johnson&#34;&gt;Robert Caro&#39;s Years of Lyndon Johnson&lt;/a&gt;. I just can&#39;t recommend this series enough! Caro has done an unbelievable amount of research, and his writing is very engaging.&lt;/p&gt;&#xA;&lt;p&gt;Master of the Senate (spanning 1167 pages!) was somehow simultaneously a description of how the Senate works and a page turner.&#xA;It&#39;s an excellent drama on how Johnson ascended to the role of Senate majority leader, and uses that power to incredible gain, passing among others a landmark civil rights bill.&#xA;Learned a lot about how legislation in the US works, too.&lt;/p&gt;&#xA;&lt;p&gt;In The Passage of Power, Johnson joins JFK as his vice president, Kennedy is assassinated, and Johnson takes power as president (this is his life-long dream!).&#xA;It ends a few months after he came to power, in which time he had accomplished a lot already.&#xA;My favorite thing to come out of this was getting a more nuanced view of JFK. He was not as likable as I thought: Far more cynical. But also incredibly courageous.&lt;/p&gt;&#xA;&lt;p&gt;I also read another biography about a politician: The Last Lion 1 about Churchill. What a remarkable man. Listened to this on 2x on audiobook while painting the house, I think I would have enjoyed it more if I&#39;d sat down and read it.&#xA;But wow, Churchill was impressive.&#xA;For example, he spent multiple stints as a war correspondent. During the Boer War in South Africa he was captured as a prisoner of war and makes an escape that&#39;s straight out of a movie.&#xA;And he was hilarious. I laughed out loud many times.&#xA;I&#39;m hoping to read The Last Lion book 2 this year.&lt;/p&gt;&#xA;&lt;h3 id=&#34;silicon-valley-history&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#silicon-valley-history&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Silicon Valley history&lt;/h3&gt;&#xA;&lt;p&gt;As always, I read some Silicon Valley history. This is probably the biggest theme going through my reading in previous years (&lt;a href=&#34;/books-2018&#34;&gt;2018&lt;/a&gt;, &lt;a href=&#34;/books-2019&#34;&gt;2019&lt;/a&gt;, &lt;a href=&#34;/books-2020&#34;&gt;2020&lt;/a&gt;, &lt;a href=&#34;/books-2021&#34;&gt;2021&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The Big Score: The Billion Dollar Story of Silicon Valley (1985) by Michael Malone. Written in 1985, so it&#39;s one of the books written during the birth of Silicon Valley as we know it. It&#39;s based on the author&#39;s interviews from his time as a tech reporter. Iconoclastic founders have been a staple of Silicon Valley since the beginning!&lt;/li&gt;&#xA;&lt;li&gt;The Founders: The Story of Paypal (2022) by Jimmy Soni. Highly enjoyable. The &lt;a href=&#34;https://en.wikipedia.org/wiki/PayPal_Mafia&#34;&gt;PayPal mafia&lt;/a&gt; is well known, so it was fun to read the origin story. Particularly interesting topics: The merger with X.com (Elon Musk&#39;s site) and Musk subsequently proposing a rewrite of the entire PayPal codebase; Max Levchin (now CEO of Affirm) fighting fraud.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;tech-related&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#tech-related&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Tech related&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Kotlin in Action (2016) by Jemerov and Isakova. I started writing Kotlin at work, and reading this was a boon to my productivity.&lt;/li&gt;&#xA;&lt;li&gt;The Mythical Man Month: Essays on Software Engineering (1975) by Fred Brooks (&lt;em&gt;reread&lt;/em&gt;). A foundational text in software engineering management. The &lt;a href=&#34;https://en.wikipedia.org/wiki/The_Mythical_Man-Month#Ideas_presented&#34;&gt;list of ideas presented is long&lt;/a&gt;, and if you&#39;re a software engineer, you&#39;re bound to have heard some.&#xA;E.g. &lt;em&gt;Adding manpower to a late software project makes it later.&lt;/em&gt;&#xA;Highly recommended for anyone in software.&lt;/li&gt;&#xA;&lt;li&gt;How Query Engines Work (2022) Andy Grove. I&#39;ve become interested in databases recently. This was a quick read about, well, how query engines work, in particular query planning. Grove (not &lt;a href=&#34;https://en.wikipedia.org/wiki/Andrew_Grove&#34;&gt;&lt;em&gt;that&lt;/em&gt; Andy Grove&lt;/a&gt;) started the DataFusion query engine project that is now part of Apache Arrow. The book has a fairly low level of detail, which is good for an overview, but the book felt a bit incomplete. Turns out the book is still in progress and &lt;a href=&#34;https://howqueryengineswork.com/&#34;&gt;available online&lt;/a&gt;, so I would recommend reading there.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;michael-lewis-books&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#michael-lewis-books&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Michael Lewis books&lt;/h3&gt;&#xA;&lt;p&gt;I rewatched both The Big Short and Moneyball this year, and was inspired to read the books behind the movies. I&#39;ve read other things by Lewis before, and his writing is interesting and easy to read.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Moneyball (2003) by Michael Lewis&lt;/li&gt;&#xA;&lt;li&gt;The Big Short (2010) by Michael Lewis&lt;/li&gt;&#xA;&lt;li&gt;The Premonition: A Pandemic Story (2021) by Michael Lewis. About the Covid pandemic, and one big takeaway from that is how badly CDC was set up to handle it. Bad incentives all around.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;other-non-fiction&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#other-non-fiction&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Other non fiction&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The Making of Prince of Persia: Journals 1985 - 1993 (2011) Jordan Mechner&lt;/li&gt;&#xA;&lt;li&gt;Working Backwards: Insights, Stories, and Secrets from Inside Amazon (2021) by Bryar and Carr. Amazon&#39;s six pager memos are well known by now, and has inspired a culture of design docs in software companies. This book describes that and other interesting Amazon management things.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;fiction&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#fiction&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Fiction&lt;/h3&gt;&#xA;&lt;p&gt;I only finished one novel in 2022. I also started Kafka&#39;s The Trial, but it was too frustrating!&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Never Let Me Go (2005) by Kazuo Ishiguro. Thought provoking and sad.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Got any recommendations? Hit me up!&lt;/p&gt;&#xA;&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;Posted an overview of the 14 books I read in 2022, and some words about my favourites! Take a look to find out how reading about the Senate can be interesting, and a previous time Musk joined a company and suggested a rewrite of the codebase &lt;a href=&#34;https://t.co/enOFO2LRuU&#34;&gt;https://t.co/enOFO2LRuU&lt;/a&gt;&lt;/p&gt;&amp;mdash; Vegard Stikbakke (@vegardstikbakke) &lt;a href=&#34;https://twitter.com/vegardstikbakke/status/1613461049541332994?ref_src=twsrc%5Etfw&#34;&gt;January 12, 2023&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;</description>
    </item>
    <item>
      <title>What&amp;#39;s coming in Go 1.18 (for LWN.net)</title>
      <link>https://vegardstikbakke.com/go-118/</link>
      <pubDate>Tue, 15 Feb 2022 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/go-118/</guid>
      <description>&lt;p&gt;I wrote a paid blog post for LWN.net on a freelance basis: &lt;a href=&#34;https://lwn.net/Articles/883602/&#34;&gt;What&#39;s coming in Go 1.18&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>My open source contributions</title>
      <link>https://vegardstikbakke.com/open-source/</link>
      <pubDate>Mon, 10 Jan 2022 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/open-source/</guid>
      <description>&lt;p&gt;I&#39;ve worked full time as a software engineer for around two and a half years, since May 2019.&#xA;Almost all of the code I&#39;ve written has been in closed-source repos, but occasionally I&#39;ve ran into something somewhere else that I was able to fix.&#xA;I compiled a list of the pull requests (PRs) I&#39;ve submitted that have been accepted upstream.&#xA;There are 15 PRs in total, and most of them are typos in documentation, but I have a few code fixes as well.&lt;/p&gt;&#xA;&lt;h2 id=&#34;python&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#python&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Python&lt;/h2&gt;&#xA;&lt;p&gt;A bug fix, bpo-38932, to a test library in the Python standard library, which &lt;a href=&#34;https://docs.python.org/3/whatsnew/changelog.html&#34;&gt;was included in Python 3.9&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/python/cpython/pull/17409&#34;&gt;https://github.com/python/cpython/pull/17409&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Two fixes to &lt;a href=&#34;https://github.com/python/typeshed&#34;&gt;typeshed&lt;/a&gt;, which is where external type annotations for the Python standard library live.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/python/typeshed/pull/4191&#34;&gt;https://github.com/python/typeshed/pull/4191&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/python/typeshed/pull/4358&#34;&gt;https://github.com/python/typeshed/pull/4358&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;others&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#others&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Others&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/alexbrainman/odbc&#34;&gt;alexbrainman/odbc&lt;/a&gt; is an &lt;a href=&#34;https://github.com/alexbrainman/odbc&#34;&gt;ODBC&lt;/a&gt; driver in Go. It failed to compile on ARM-based Macs due to the Homebrew path for these being different. &lt;a href=&#34;https://github.com/alexbrainman/odbc/pull/168&#34;&gt;https://github.com/alexbrainman/odbc/pull/168&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Documentation fix to FastAPI, which is an excellent API framework in Python. &lt;a href=&#34;https://github.com/tiangolo/fastapi/pull/1015&#34;&gt;https://github.com/tiangolo/fastapi/pull/1015&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;typos&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#typos&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Typos&lt;/h2&gt;&#xA;&lt;p&gt;I&#39;m really good at spotting typos, so I&#39;ve fixed a number of these.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/emcconville/wand/pull/464&#34;&gt;https://github.com/emcconville/wand/pull/464&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/googleapis/python-storage/pull/84&#34;&gt;https://github.com/googleapis/python-storage/pull/84&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/sourcegraph/about/pull/932&#34;&gt;https://github.com/sourcegraph/about/pull/932&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/elastic/elasticsearch-py/pull/1277&#34;&gt;https://github.com/elastic/elasticsearch-py/pull/1277&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/openethereum/openethereum.github.io/pull/11&#34;&gt;https://github.com/openethereum/openethereum.github.io/pull/11&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/benbjohnson/litestream.io/pull/16&#34;&gt;https://github.com/benbjohnson/litestream.io/pull/16&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/benhoyt/benhoyt.github.com/pull/5&#34;&gt;https://github.com/benhoyt/benhoyt.github.com/pull/5&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/lib/pq/pull/1034&#34;&gt;https://github.com/lib/pq/pull/1034&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/ethereum/go-ethereum/pull/22769&#34;&gt;https://github.com/ethereum/go-ethereum/pull/22769&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/ethereum/go-ethereum/pull/22744&#34;&gt;https://github.com/ethereum/go-ethereum/pull/22744&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Books I read in 2021</title>
      <link>https://vegardstikbakke.com/books-2021/</link>
      <pubDate>Sun, 02 Jan 2022 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/books-2021/</guid>
      <description>&lt;p&gt;Below is a list of the 12 books I read this year, spanning 4244 pages (&lt;a href=&#34;https://www.goodreads.com/user/year_in_books/2021/3400170&#34;&gt;according to Goodreads&lt;/a&gt;).&#xA;This is less than &lt;a href=&#34;/books-2018&#34;&gt;2018 (16)&lt;/a&gt;, &lt;a href=&#34;/books-2019&#34;&gt;2019 (40)&lt;/a&gt;, and &lt;a href=&#34;/books-2020&#34;&gt;2020 (23)&lt;/a&gt;, but considering this is my first full year with a toddler, I&#39;m pleased!&lt;/p&gt;&#xA;&lt;p&gt;Those in bold were my favourites.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Hard Drive: Bill Gates and the Making of the Microsoft Empire&lt;/strong&gt; by James Wallace and Jim Erickson&lt;/li&gt;&#xA;&lt;li&gt;The Dark Forest by Liu Cixin&lt;/li&gt;&#xA;&lt;li&gt;How the Internet Happened by Brian McCullough&lt;/li&gt;&#xA;&lt;li&gt;Staff Engineer: Leadership Beyond the Management Track by Will Larson&lt;/li&gt;&#xA;&lt;li&gt;The Art of Readable Code by Dustin Boswell and Trevor Foucher&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Amazon Unbound&lt;/strong&gt; by Brad Stone&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Writing an Interpreter in Go&lt;/strong&gt; by Thorsten Ball (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Man from the Future: The Visionary Life of John von Neumann&lt;/strong&gt; by Ananyo Bhattacharya&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Means of Ascent&lt;/strong&gt; by Robert Caro&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Foundation&lt;/strong&gt; by Isaac Asimov (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Foundation and Empire by Isaac Asimov (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Second Foundation by Isaac Asimov (&lt;em&gt;reread&lt;/em&gt;)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;And here are books I started but didn&#39;t or haven&#39;t finished.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Selfish Reasons to Have More Kids by Bryan Caplan&lt;/li&gt;&#xA;&lt;li&gt;Game Theory: A Nontechnical Introduction by Morton Davis&lt;/li&gt;&#xA;&lt;li&gt;The Box by Marc Levinson&lt;/li&gt;&#xA;&lt;li&gt;The Innovator&#39;s Dilemma by Clayton Christensen&lt;/li&gt;&#xA;&lt;li&gt;The Rise of Christianity by Rodney Stark&lt;/li&gt;&#xA;&lt;li&gt;Working Backwards: Insights, Stories, and Secrets from Inside Amazon by Colin Bryar and Bill Carr&lt;/li&gt;&#xA;&lt;li&gt;Mindstorms by Seymour Papert&lt;/li&gt;&#xA;&lt;li&gt;The Contrarian: Peter Thiel and Silicon Valley&#39;s Pursuit of Power by Max Chafkin&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;2021 book thread&lt;/p&gt;&amp;mdash; Vegard Stikbakke (@vegardstikbakke) &lt;a href=&#34;https://twitter.com/vegardstikbakke/status/1350897829959577600?ref_src=twsrc%5Etfw&#34;&gt;January 17, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;</description>
    </item>
    <item>
      <title>til.vegardstikbakke.com</title>
      <link>https://vegardstikbakke.com/til/</link>
      <pubDate>Tue, 10 Aug 2021 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/til/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://til.simonwillison.net/&#34;&gt;Simon Willison&lt;/a&gt; and others (&lt;a href=&#34;https://github.com/jbranchaud/til&#34;&gt;Josh Branchaud&lt;/a&gt;, &lt;a href=&#34;https://til.codeinthehole.com/&#34;&gt;David Winterbottom&lt;/a&gt;) have started posting their Today I Learned snippets, or TILs for short.&lt;/p&gt;&#xA;&lt;p&gt;Occasionally over the past couple years, I&#39;ve noted down a useful command, fact or code snippet in a folder.&#xA;Instead of just keeping these in a private Git repo, I decided to do the same:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;I created a site on Netlify with the subdomain - which was super easy, by the way!&lt;/li&gt;&#xA;&lt;li&gt;I copied the design and scripts that Winterbottom uses - &lt;a href=&#34;https://github.com/vegarsti/til&#34;&gt;here&#39;s my Git repo&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;I migrated my existing notes to the new format.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;And all of a sudden I had a site of pre-populated snippets!&lt;/p&gt;&#xA;&lt;p&gt;Head over to &lt;a href=&#34;https://til.vegardstikbakke.com&#34;&gt;til.vegardstikbakke.com&lt;/a&gt; to check it out!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Waiting for dependencies in tests</title>
      <link>https://vegardstikbakke.com/dependencies/</link>
      <pubDate>Fri, 28 May 2021 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/dependencies/</guid>
      <description>&lt;p&gt;As part of my development workflow, I usually run some integration tests.&#xA;An integration test means that code interacts with some outside dependency.&#xA;This could be a temporary Postgres server in a Docker container, or it could be an HTTP server.&#xA;These dependencies usually take some time to start.&#xA;In particular, enough time to start that our tests would fail if we ran them immediately after starting our dependencies.&lt;/p&gt;&#xA;&lt;p&gt;To make this concrete, let&#39;s use &lt;a href=&#34;https://docs.docker.com/compose/&#34;&gt;docker-compose&lt;/a&gt; to start a Postgres server in a Docker container.&#xA;Our &lt;code&gt;docker-compose.yml&lt;/code&gt; becomes&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:13-alpine&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;5432:5432&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;admin&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;admin&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and we can start it by doing &lt;code&gt;docker-compose up&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;We also want a &lt;code&gt;Makefile&lt;/code&gt; to perform the whole test, including the setup.&#xA;The structure of our &lt;code&gt;Makefile&lt;/code&gt; is&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;start Docker containers&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;wait until dependencies are ready&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;execute tests&lt;/li&gt;&#xA;&lt;li&gt;shut down Docker containers&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The lazy way to do step 2 is to wait a set amount of time before executing tests, typically by running the shell command &lt;code&gt;sleep N&lt;/code&gt;.&#xA;To be sure that the dependencies are indeed ready when we start our tests, we will want to set &lt;code&gt;N&lt;/code&gt; to a safe upper bound on the depency&#39;s startup time.&#xA;If we run locally, we probably have the Docker containers cached, so &lt;code&gt;docker-compose up&lt;/code&gt; might not take long.&#xA;However, if we run our CI in Github Actions, the containers will not be cached, and so the wait time is longer and more unpredictable.&#xA;This means that if we set the time to a comfortable upper bound so we sleep long enough in CI, we risk wasting time each time we run our tests locally.&lt;/p&gt;&#xA;&lt;p&gt;But there are better solutions!&lt;/p&gt;&#xA;&lt;h2 id=&#34;until&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#until&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&lt;code&gt;until&lt;/code&gt;&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://linuxcommand.org/lc3_man_pages/untilh.html&#34;&gt;&lt;code&gt;until&lt;/code&gt; shell command&lt;/a&gt; will run a command until it succeeds.&#xA;Like a &lt;code&gt;while&lt;/code&gt; loop but with the predicate switched.&#xA;If we find a shell command that returns exit code 0 (indicating success) if the dependencies are ready, and not 0 if they are not ready, we can put this inside an &lt;code&gt;until&lt;/code&gt; loop.&lt;/p&gt;&#xA;&lt;p&gt;It turns out that the Postgres client &lt;code&gt;psql&lt;/code&gt; comes with a command called &lt;a href=&#34;https://www.postgresql.org/docs/13/app-pg-isready.html&#34;&gt;&lt;code&gt;pg_isready&lt;/code&gt;&lt;/a&gt;, which will do just that:&#xA;It pings a Postgres server and returns a non-zero exit code if it&#39;s not ready.&lt;/p&gt;&#xA;&lt;p&gt;The complete Makefile becomes&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;make &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;docker-compose up -V -d postgres&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;until&lt;/span&gt; pg_isready --host localhost --port 5432&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&#x9;&#x9;sleep 0.1&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;se&#34;&gt;&lt;/span&gt;&#x9;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;my test here&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;docker-compose down&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Try it yourself by cloning &lt;a href=&#34;https://github.com/vegarsti/until&#34;&gt;this Github repo&lt;/a&gt; and running &lt;code&gt;make&lt;/code&gt;!&lt;/p&gt;&#xA;&lt;h2 id=&#34;bonus-http-server&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#bonus-http-server&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Bonus: HTTP server&lt;/h2&gt;&#xA;&lt;p&gt;If we want to wait for an HTTP server to be responsive, we can instead run this &lt;a href=&#34;https://curl.se/&#34;&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/a&gt; command:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;curl --output /dev/null --silent --head --fail http://localhost:${PORT}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;which becomes the following in a Makefile:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;until $$(curl --output /dev/null --silent --head --fail &amp;quot;http://localhost:8080&amp;quot;); do \&#xA;&#x9;sleep 0.1; \&#xA;done&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
    </item>
    <item>
      <title>Books I read in 2020</title>
      <link>https://vegardstikbakke.com/books-2020/</link>
      <pubDate>Mon, 04 Jan 2021 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/books-2020/</guid>
      <description>&lt;p&gt;Below is a list of the 23 books I read this year (&lt;a href=&#34;https://www.goodreads.com/user/year_in_books/2020/3400170&#34;&gt;8151 pages&lt;/a&gt;).&#xA;Those in bold were my favourites.&#xA;See also what I read in &lt;a href=&#34;/books-2018&#34;&gt;2018&lt;/a&gt; and &lt;a href=&#34;/books-2019&#34;&gt;2019&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Flow by Mihaly Csikszentmihalyi&lt;/li&gt;&#xA;&lt;li&gt;Super Pumped by Mike Isaac&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Educated&lt;/strong&gt; by Tara Westover&lt;/li&gt;&#xA;&lt;li&gt;In the Company of Giants by Rama Dev Jager and Ortiz Rafael&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Uncanny Valley&lt;/strong&gt; by Anna Wiener&lt;/li&gt;&#xA;&lt;li&gt;Inside the Mind of Xi Jinping by François Bougon&lt;/li&gt;&#xA;&lt;li&gt;Founders at Work by Jessica Livingston&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Writing an Interpreter in Go&lt;/strong&gt; by Thorsten Ball&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Writing a Compiler in Go&lt;/strong&gt; by Thorsten Ball&lt;/li&gt;&#xA;&lt;li&gt;Debugging by David J. Agans&lt;/li&gt;&#xA;&lt;li&gt;Facebook by Steven Levy&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A Philosophy of Software Design&lt;/strong&gt; (reread) by John Osterhout&lt;/li&gt;&#xA;&lt;li&gt;The Three-Body Problem by Cixin Liu&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Art of Doing Science and Engineering&lt;/strong&gt; by Richard Hamming&lt;/li&gt;&#xA;&lt;li&gt;Netflixed by Gina Keating&lt;/li&gt;&#xA;&lt;li&gt;The J Primer by Eric Iverson&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ten Essays on FizzBuzz&lt;/strong&gt; by Joel Grus&lt;/li&gt;&#xA;&lt;li&gt;The Infinite Machine by Camila Russo&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Go Programming Language&lt;/strong&gt; by Alan A. A. Donovan and Brian Kernighan&lt;/li&gt;&#xA;&lt;li&gt;What Every Web Developer Should Know About HTTP by K. Scott Allen&lt;/li&gt;&#xA;&lt;li&gt;The Perfect Store: Inside eBay by Adam Cohen&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Designing Data-Intensive Applications&lt;/strong&gt; by Martin Kleppmann&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Path to Power&lt;/strong&gt; by Robert Caro&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Some interesting people&#39;s 2020 books:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://danromero.org/2020-booklist.html&#34;&gt;Dan Romero&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://notes.eatonphil.com/year-in-books-2020.html&#34;&gt;Phil Eaton&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://notebook.maryrosecook.com/BooksIreadin2020.html&#34;&gt;Mary Rose Cook&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;en&#34; dir=&#34;ltr&#34;&gt;2020 book thread&lt;/p&gt;&amp;mdash; Vegard Stikbakke (@vegardstikbakke) &lt;a href=&#34;https://twitter.com/vegardstikbakke/status/1219219790549614592?ref_src=twsrc%5Etfw&#34;&gt;January 20, 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;</description>
    </item>
    <item>
      <title>How do Unix pipes work?</title>
      <link>https://vegardstikbakke.com/how-do-pipes-work-sigpipe/</link>
      <pubDate>Sat, 21 Mar 2020 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/how-do-pipes-work-sigpipe/</guid>
      <description>&lt;p&gt;Pipes are cool!&#xA;We saw how handy they are in a &lt;a href=&#34;/unix&#34;&gt;previous blog post&lt;/a&gt;.&#xA;Let&#39;s look at a typical way to use the pipe operator.&#xA;We have some output, and we want to look at the first lines of the output.&#xA;Let&#39;s &lt;a href=&#34;https://www.gutenberg.org/files/28054/28054-0.txt&#34;&gt;download&lt;/a&gt; &lt;a href=&#34;https://en.wikipedia.org/wiki/Lazy_evaluation&#34;&gt;The Brothers Karamazov&lt;/a&gt; by Fyodor Dostoevsky, a fairly long novel.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; wc -l karamazov.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;m&#34;&gt;36484&lt;/span&gt; karamazov.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we &lt;code&gt;cat&lt;/code&gt; this file, it will be printed to the terminal.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; cat brothers_karamazov.txt&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;... many lines of text!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;***FINIS***&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It takes a noticeable amount of time to finish.&#xA;Now let&#39;s just look at the first two lines by piping it into &lt;code&gt;head&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; cat karamazov.txt &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -n &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The Project Gutenberg EBook of The Brothers Karamazov by Fyodor&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dostoyevsky&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now it&#39;s done in an instant.&#xA;It seems that the &lt;code&gt;cat&lt;/code&gt; operation terminates when &lt;code&gt;head&lt;/code&gt; is done!&#xA;Of course, &lt;code&gt;head -n 2&lt;/code&gt; only needs two lines of input to output what it&#39;s supposed to output.&#xA;But how does &lt;code&gt;cat&lt;/code&gt; know to stop when &lt;code&gt;head&lt;/code&gt; is finished?&lt;/p&gt;&#xA;&lt;p&gt;In this blog post, we&#39;ll learn a bit about how pipes work, and write a small &lt;code&gt;cat&lt;/code&gt; clone in Python and Go.&lt;/p&gt;&#xA;&lt;hr /&gt;&#xA;&lt;p&gt;Processes in a pipeline are started simultaneously.&#xA;We can confirm this by running&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; sleep &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;in one terminal window, and taking a look at running processes with &lt;code&gt;ps&lt;/code&gt; in another.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; ps&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  PID TTY           TIME CMD&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;52892&lt;/span&gt; ttys007    0:00.00 sleep &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;52893&lt;/span&gt; ttys007    0:00.00 head&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From the Unix manual page on pipes, &lt;a href=&#34;https://linux.die.net/man/7/pipe&#34;&gt;&lt;code&gt;man(7) pipe&lt;/code&gt;&lt;/a&gt;, we learn that if we pipe &lt;code&gt;process1&lt;/code&gt; into &lt;code&gt;process2&lt;/code&gt;, the second process will wait (block) until it receives input.&#xA;Furthermore, if &lt;code&gt;process2&lt;/code&gt; is finished, it closes its end of the pipe.&#xA;This will cause a &lt;code&gt;SIGPIPE&lt;/code&gt; signal to be generated for &lt;code&gt;process1&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;writing-a-cat-clone-in-python&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#writing-a-cat-clone-in-python&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Writing a &lt;code&gt;cat&lt;/code&gt; clone in Python&lt;/h2&gt;&#xA;&lt;p&gt;Let&#39;s try to write a simple &lt;code&gt;cat&lt;/code&gt; clone that mimics this behavior.&#xA;Instead of reading from file, let&#39;s read from standard input.&#xA;That means we should be able to pipe into it.&#xA;In Python, standard input is found in &lt;a href=&#34;https://docs.python.org/3/library/sys.html#sys.stdin&#34;&gt;&lt;code&gt;sys.stdin&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# cat.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This behaves the same as the regular &lt;code&gt;cat&lt;/code&gt; command if we pipe into it.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cat karamazov &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; python cat.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;... many lines of text!&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;***FINIS*&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But does it also stop when we pipe it into &lt;code&gt;head&lt;/code&gt;?&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; cat karamazov &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; python cat.py &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -n &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The Project Gutenberg EBook of The Brothers Karamazov by Fyodor&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dostoyevsky&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Traceback &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;most recent call last&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  File &lt;span class=&#34;s2&#34;&gt;&amp;#34;cat.py&amp;#34;&lt;/span&gt;, line 4, in &amp;lt;module&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    print&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;line, &lt;span class=&#34;nv&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BrokenPipeError: &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Errno 32&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Broken pipe&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Exception ignored in: &amp;lt;_io.TextIOWrapper &lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;lt;stdout&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;mode&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;w&amp;#39;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;encoding&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;UTF-8&amp;#39;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BrokenPipeError: &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Errno 32&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Broken pipe&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see that we get an error &lt;code&gt;BrokenPipeError&lt;/code&gt;.&#xA;But if we look closely at the output here, we see that it&#39;s printed twice.&#xA;We can try catching the error with a &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;except&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# cat.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;BrokenPipeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;pass&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we only get the second part of the error output from before.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; cat karamazov.txt &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; python cat.py  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -n &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The Project Gutenberg EBook of The Brothers Karamazov by Fyodor&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dostoyevsky&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Exception ignored in: &amp;lt;_io.TextIOWrapper &lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;lt;stdout&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;mode&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;w&amp;#39;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;encoding&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;UTF-8&amp;#39;&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;BrokenPipeError: &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Errno 32&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; Broken pipe&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since we know that a terminated process to the right of a pipe sends a &lt;code&gt;SIGPIPE&lt;/code&gt; when it&#39;s done, maybe this means that this signal isn&#39;t handled properly?&lt;/p&gt;&#xA;&lt;p&gt;Indeed, a quick search leads us to a section of the Python documentation, namely &lt;a href=&#34;https://docs.python.org/3/library/signal.html#note-on-sigpipe&#34;&gt;Note on &lt;code&gt;SIGPIPE&lt;/code&gt;&lt;/a&gt;.&#xA;It gives a recipe for handling it.&#xA;Our program now becomes&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# cat.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;os&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;BrokenPipeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;devnull&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;devnull&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;O_WRONLY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dup2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;devnull&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdout&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fileno&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and it works as we want, by getting rid of the pesky error message.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; cat karamazov.txt &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; python cat.py  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -n &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The Project Gutenberg EBook of The Brothers Karamazov by Fyodor&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dostoyevsky&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But an explanation of how this works would be nice.&#xA;The recipe has a comment saying that&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Python flushes standard streams on exit; redirect remaining output to devnull to avoid another &lt;code&gt;BrokenPipeError&lt;/code&gt; at shutdown.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;What we do here, apparently, is redirect the remaining error output to &lt;code&gt;/dev/null&lt;/code&gt;.&#xA;&lt;code&gt;/dev/null&lt;/code&gt; refers to [https://en.wikipedia.org/wiki/Null_device](the null device), which accepts output, but discards it all.&#xA;In other words, to not print something, we can redirect output there.&lt;/p&gt;&#xA;&lt;p&gt;The first line in the &lt;code&gt;except BrokenPipeError&lt;/code&gt; block opens &lt;code&gt;/dev/null&lt;/code&gt;.&#xA;The second line calls &lt;a href=&#34;https://docs.python.org/3/library/os.html#os.dup2&#34;&gt;&lt;code&gt;os.dup2&lt;/code&gt;&lt;/a&gt;, which takes two file descriptors.&#xA;It &lt;em&gt;&amp;quot;duplicate[s] file descriptor &lt;code&gt;fd&lt;/code&gt; to &lt;code&gt;fd2&lt;/code&gt;, closing the latter first if necessary.&amp;quot;&lt;/em&gt;&#xA;So in the second line we duplicate &lt;code&gt;/dev/null&lt;/code&gt; into standard output, and possibly close standard output first.&lt;/p&gt;&#xA;&lt;p&gt;But I don&#39;t really understand this.&#xA;It turns out that another way of silencing the output is to close standard error.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;BrokenPipeError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;writing-a-cat-clone-in-go&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#writing-a-cat-clone-in-go&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;Writing a &lt;code&gt;cat&lt;/code&gt; clone in Go&lt;/h2&gt;&#xA;&lt;p&gt;I want to learn some Go, so let&#39;s try writing the same program in it.&#xA;In Go, we can apparently simply copy from standard in to standard out.&#xA;The first program becomes a tidy one liner.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// cat.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;io&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When we run this, it also complains about &lt;code&gt;SIGPIPE&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;gt; cat karamazov.txt &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; go run cat.go &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -n &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The Project Gutenberg EBook of The Brothers Karamazov by Fyodor&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Dostoyevsky&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;signal: broken pipe&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Turns out ignoring the &lt;code&gt;SIGPIPE&lt;/code&gt; signal also is one line.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// cat.go&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;io&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;os/signal&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;&amp;#34;syscall&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;signal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Ignore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;syscall&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;SIGPIPE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stdin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I hope you learned something reading this!&lt;/p&gt;&#xA;&lt;p&gt;This post &lt;a href=&#34;https://news.ycombinator.com/item?id=22647539&#34;&gt;got some attention on Hacker News&lt;/a&gt;, and many valid concerns were raised.&#xA;You should read the comments there!&lt;/p&gt;&#xA;&lt;p&gt;If you liked this post, you might like my other post &lt;a href=&#34;../unix&#34;&gt;&lt;strong&gt;Problem solving with Unix commands&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>I fixed a bug in Python!</title>
      <link>https://vegardstikbakke.com/python-contribution/</link>
      <pubDate>Mon, 09 Mar 2020 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/python-contribution/</guid>
      <description>&lt;p&gt;This fall, my colleague and I discovered a bug in the unittest module in Python.&#xA;In January, &lt;a href=&#34;https://github.com/python/cpython/pull/17409&#34;&gt;my PR&lt;/a&gt; was merged onto the cpython master!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Books I read in 2019</title>
      <link>https://vegardstikbakke.com/books-2019/</link>
      <pubDate>Sun, 05 Jan 2020 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/books-2019/</guid>
      <description>&lt;p&gt;This year I read 40 books, surpassing my goal of 30 (&lt;a href=&#34;https://www.goodreads.com/user/year_in_books/2019/3400170&#34;&gt;see my year in books on Goodreads&lt;/a&gt;).&#xA;I&#39;m pleasantly surprised at how much reading it&#39;s possible to get in, just by reading when commuting and in various idle windows during the afternoon.&#xA;&lt;a href=&#34;/books-2018&#34;&gt;Last year I read 5k pages over 16 books&lt;/a&gt;, whereas this year I read 12k (12137).&#xA;Below is a list of the books I read.&#xA;Those in bold are especially recommended, either because they were particularly enjoying or interesting.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Show Stopper by G. Pascal Zachary&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Stubborn Attachments&lt;/strong&gt; by Tyler Cowen&lt;/li&gt;&#xA;&lt;li&gt;Sapiens by Yuval Noah Harari&lt;/li&gt;&#xA;&lt;li&gt;Creative Selection by Ken Kocienda&lt;/li&gt;&#xA;&lt;li&gt;The Great Stagnation by Tyler Cowen&lt;/li&gt;&#xA;&lt;li&gt;We Are The Nerds by Christine Lagorio-Chafkin&lt;/li&gt;&#xA;&lt;li&gt;High Growth Handbook by Elad Gil&lt;/li&gt;&#xA;&lt;li&gt;Shoe Dog by Phil Knight&lt;/li&gt;&#xA;&lt;li&gt;Hackers and Painters by Paul Graham&lt;/li&gt;&#xA;&lt;li&gt;AI Superpowers by Kai-Fu Lee&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;How Asia Works&lt;/strong&gt; by Joe Studwell&lt;/li&gt;&#xA;&lt;li&gt;Jeg vil dø - et essay om dødsønske og dødshjelp, mennesket og dets samfunn by Andreas and Randi Espegren Masvie&lt;/li&gt;&#xA;&lt;li&gt;The Soul of a New Machine by Tracy Kidder&lt;/li&gt;&#xA;&lt;li&gt;The Crying of Lot 49 by Thomas Pynchon&lt;/li&gt;&#xA;&lt;li&gt;In the Beginning...Was the Command Line by Neal Stephenson&lt;/li&gt;&#xA;&lt;li&gt;Working by Robert A. Caro&lt;/li&gt;&#xA;&lt;li&gt;Big Business by Tyler Cowen&lt;/li&gt;&#xA;&lt;li&gt;Japan: The Story of a Nation by Edwin Reischauer&lt;/li&gt;&#xA;&lt;li&gt;12 Rules for Life by Jordan Peterson&lt;/li&gt;&#xA;&lt;li&gt;The Effective Engineer by Edmond Lau&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Range&lt;/strong&gt; by David Epstein&lt;/li&gt;&#xA;&lt;li&gt;An Elegant Puzzle by Will Larson&lt;/li&gt;&#xA;&lt;li&gt;Gambling with Other People&#39;s Money by Russ Roberts&lt;/li&gt;&#xA;&lt;li&gt;Brave New World by Aldous Huxley&lt;/li&gt;&#xA;&lt;li&gt;Fuglane (The Birds) by Tarjei Vesaas&lt;/li&gt;&#xA;&lt;li&gt;Good to Great by Jim Collins&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Power Broker&lt;/strong&gt; by Robert A. Caro&lt;/li&gt;&#xA;&lt;li&gt;Finite and Infinite Games by James P. Carse&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Crime and Punishment&lt;/strong&gt; by Fyodor Dostoyevsky&lt;/li&gt;&#xA;&lt;li&gt;Accidental Empires by Robert Cringely&lt;/li&gt;&#xA;&lt;li&gt;How I Sold My Business by Alvin Alexander&lt;/li&gt;&#xA;&lt;li&gt;Secrets of Sand Hill Road by Scott Kupor&lt;/li&gt;&#xA;&lt;li&gt;Resilient Management by Lara Hogan&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;High Output Management&lt;/strong&gt; by Andrew S. Grove&lt;/li&gt;&#xA;&lt;li&gt;Thinking in Systems by Donella H. Meadows&lt;/li&gt;&#xA;&lt;li&gt;Resume Speed by Lawrence Block&lt;/li&gt;&#xA;&lt;li&gt;The Man Who Solved the Market by Gregory Zuckerman&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Dream Machine&lt;/strong&gt; by M. Mitchell Waldrop&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;UNIX: A History and a Memoir&lt;/strong&gt; by Brian Kernighan&lt;/li&gt;&#xA;&lt;li&gt;Start Small, Stay Small: A Developer&#39;s Guide to Launching a Startup by Rob Walling&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Op-ed in Norway’s leading business newspaper about university admissions</title>
      <link>https://vegardstikbakke.com/dn-op-ed/</link>
      <pubDate>Wed, 24 Jul 2019 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/dn-op-ed/</guid>
      <description>&lt;p&gt;I wrote an op-ed in DN, the biggest Norwegian financial daily newspaper about how entrance to Norwegian universities is broken and extremely expensive for the Norwegian state: &lt;a href=&#34;https://www.dn.no/innlegg/utdannelse/samordna-opptak/medisin/forbedring-av-vitnemal-gir-meningslos-slosing/2-1-643154&#34;&gt;https://www.dn.no/innlegg/utdannelse/samordna-opptak/medisin/forbedring-av-vitnemal-gir-meningslos-slosing/2-1-643154&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Problem solving with Unix commands</title>
      <link>https://vegardstikbakke.com/unix/</link>
      <pubDate>Wed, 13 Feb 2019 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/unix/</guid>
      <description>&lt;p&gt;I am starting to realize that the Unix command-line toolbox can fix absolutely any problem related to text wrangling. Let me tell you about a problem I had, and how I used some Unix command-line utilities to solve it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problem&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#the-problem&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;The problem&lt;/h2&gt;&#xA;&lt;p&gt;I&#39;m working on research for my master thesis. As with many statisticians, I am running a lot of simulations. I first simulate some data according to some numerical seed (to ensure reproducibility), and then use an algorithm to estimate something based on that data. For each simulation run, I create some files, typically like so:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0001_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0001_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sometimes a run fails. This doesn&#39;t really matter in this case: For any failed simulation, I can just do another one. For the &lt;code&gt;0001&lt;/code&gt; data, I had a successful run with algorithm &lt;code&gt;A&lt;/code&gt;. Therefore I want to use the &lt;code&gt;0001&lt;/code&gt; data on algorithm &lt;code&gt;B&lt;/code&gt; as well. But I need to keep track of which runs of &lt;code&gt;A&lt;/code&gt; have failed.&lt;/p&gt;&#xA;&lt;p&gt;After running algorithm &lt;code&gt;A&lt;/code&gt; on a lot of data, I end up with a large list of files like&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0001_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0001_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0002_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0002_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0003_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0003_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0004_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0005_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0005_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0006_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0006_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0007_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0007_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0008_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0009_data.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0009_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dataset-directory/0499_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The astute observer will note that the file for algorithm &lt;code&gt;A&lt;/code&gt; on data &lt;code&gt;0004&lt;/code&gt; and &lt;code&gt;0008&lt;/code&gt; are missing. &lt;strong&gt;How can I get a list of all the numbers for which &lt;code&gt;A&lt;/code&gt; didn&#39;t succeed?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;I certainly could go over manually, but that would be error prone, and incredibly boring. It&#39;s much better to write a program to do it!&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-solution&#34;&gt;&lt;a class=&#34;heading-anchor&#34; href=&#34;#the-solution&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;The solution&lt;/h2&gt;&#xA;&lt;p&gt;To be obtuse: Those that didn&#39;t succeed are the numbers from &lt;code&gt;0001&lt;/code&gt; to &lt;code&gt;0500&lt;/code&gt; except those that succeeded. And one handy command to get a list of numbers is &lt;code&gt;seq&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$&amp;gt; seq &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;6&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;7&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;9&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(If only one number is given, it is implied that the sequence starts with &lt;code&gt;1&lt;/code&gt;. &lt;code&gt;seq 2 10&lt;/code&gt; would do what you think it would, as well.)&lt;/p&gt;&#xA;&lt;p&gt;Now, if we can get a list of all the successful runs, we should be able to get what we want by cross-checking the list of successful runs with a &lt;code&gt;seq&lt;/code&gt; command which prints all possible numbers!&lt;/p&gt;&#xA;&lt;p&gt;Most command-line utilities do one pretty specific thing. For example, with &lt;code&gt;cut&lt;/code&gt; you can get the characters on specific locations on each line&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$&amp;gt; cat text&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Lorem ipsum&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dolor sit amet&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$&amp;gt; cat text &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; cut -c 2-5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;orem&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;olor&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice here the use of the so-called pipe operator &lt;code&gt;|&lt;/code&gt;. Like I said, most utilities do one specific thing, and it does that thing well. The neat thing is that these can be combined. By using these pipes, the output from the command to the left of the pipe is directed to the command to the right. Note that these commands treat the input as a stream of lines, which is often really handy.&lt;/p&gt;&#xA;&lt;p&gt;We can get a list of the successful file names by piping the list of files into a &lt;code&gt;grep&lt;/code&gt; command, which is a command which can use regular expressions. Since all files start with an equal length of 4 digits, we can match these to the regular expression &lt;code&gt;\d\d\d\d&lt;/code&gt;, matching 4 digits in a row, and add the file ending for the &lt;code&gt;A&lt;/code&gt; algorithm to the regular expression. To get the list of files with one line for each file, we can simply do &lt;code&gt;ls&lt;/code&gt;. (Although &lt;code&gt;ls&lt;/code&gt; doesn&#39;t give each file its own line when calling it separately, it turns out that piping the output from &lt;code&gt;ls&lt;/code&gt; will.)&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$&amp;gt; ls dataset-directory &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s1&#34;&gt;&amp;#39;\d\d\d\d_A.csv&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0009_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0001_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0002_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0005_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0007_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0003_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0006_A.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For some reason, these show up in a scrambled order after using &lt;code&gt;grep&lt;/code&gt;. We can use &lt;code&gt;sort&lt;/code&gt; to fix that. And we are only interested in the numbers, so we can use &lt;code&gt;cut -c 1-4&lt;/code&gt; to extract the number parts.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$&amp;gt; ls dataset-directory &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s1&#34;&gt;&amp;#39;\d\d\d\d_A.csv&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sort &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; cut -c 1-4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0001&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0002&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0003&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0005&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0006&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0007&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0009&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0499&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These numbers aren&#39;t exactly the same as the numbers from the &lt;code&gt;seq&lt;/code&gt; command, since these are zero-padded. Therefore we write a quick Python script to parse them as integers.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# parse.py&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;sys&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stdin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, piping into this script will give us the numbers that we want:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$&amp;gt; ls dataset-directory &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s1&#34;&gt;&amp;#39;\d\d\d\d_A.csv&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; cut -c 1-4 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; python3 parse.py&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;6&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;7&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;9&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;499&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&#39;re getting there! Now we have to figure out how to cross-check these lists of numbers. Luckily, there exists a command called &lt;code&gt;comm&lt;/code&gt;, which checks for &lt;u&gt;comm&lt;/u&gt;on characters in two input streams. To get the input of a sequence of commands such as the one above, we can evaluate it and redirect it, which we do by wrapping it in &lt;code&gt;&amp;lt;(...)&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$&amp;gt; comm &amp;lt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ls dataset-directory &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s1&#34;&gt;&amp;#39;\d\d\d\d_A.csv&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; cut -c 1-4 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; python3 parse.py&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &amp;lt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;seq 500&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;m&#34;&gt;6&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;m&#34;&gt;7&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;m&#34;&gt;9&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;m&#34;&gt;500&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This output is a bit disorienting. If we read the manual of &lt;code&gt;comm&lt;/code&gt; (by doing &lt;code&gt;man comm&lt;/code&gt;), we see that &lt;code&gt;comm&lt;/code&gt; &amp;quot;produces three text columns as output: lines only in file1; lines only in file2; and lines in both files.&amp;quot; To suppress column 1 -- which is empty, since no numbers are only from the file list -- call &lt;code&gt;comm&lt;/code&gt; with the flag &lt;code&gt;-1&lt;/code&gt;. And since we are not interested in the numbers which are in both streams, we suppress with the &lt;code&gt;-3&lt;/code&gt; flag as well.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$&amp;gt; comm -1 -3 &amp;lt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;ls dataset-directory &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s1&#34;&gt;&amp;#39;\d\d\d\d_A.csv&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; cut -c 1-4 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; python3 parse.py&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &amp;lt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;seq 500&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;500&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And we&#39;re done!&lt;/p&gt;&#xA;&lt;p&gt;Update: This post generated &lt;a href=&#34;https://news.ycombinator.com/item?id=19160659&#34;&gt;some interesting discussion on Hacker News&lt;/a&gt;. There are many ways to solve this problem, and the way I did it is probably not the best. Be sure to check it out for tips on how to improve. &lt;a href=&#34;https://dev.to/vegarsti/problem-solving-with-unix-commands-4j8l&#34;&gt;I was also asked to post this on dev.to, which I did&lt;/a&gt;!&lt;/p&gt;&#xA;&lt;p&gt;If you liked this post, you might like my other post &lt;a href=&#34;../how-do-pipes-work-sigpipe/&#34;&gt;&lt;strong&gt;How do Unix pipes work?&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Books I read in 2018</title>
      <link>https://vegardstikbakke.com/books-2018/</link>
      <pubDate>Mon, 21 Jan 2019 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/books-2018/</guid>
      <description>&lt;p&gt;As a kid I read a lot of books. Not so the last couple of years. In 2018 I managed to get thoroughly into reading again. There is just so much to learn, and the more I read, the more I want to read. If you&#39;re interested, &lt;a href=&#34;https://www.goodreads.com/user/show/3400170-vegard-stikbakke&#34;&gt;this is my Goodreads account&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;In 2018, I apparently read 5267 pages total over 16 books (see &lt;a href=&#34;https://www.goodreads.com/user/year_in_books/2018/3400170&#34;&gt;My Year in Books on Goodreads&lt;/a&gt;). I&#39;m pretty satisfied with that. The themes of the books seem to be mainly business, startups, tech and biographies.&lt;/p&gt;&#xA;&lt;p&gt;Here&#39;s a list of the books I read. Those in bold are especially recommended.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Why We Sleep&lt;/strong&gt; by Matthew Walker&lt;/li&gt;&#xA;&lt;li&gt;The Lean Startup by Eric Ries&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The Everything Store&lt;/strong&gt; by Brad Stone&lt;/li&gt;&#xA;&lt;li&gt;The Hard Thing About Hard Things by Ben Horowitz&lt;/li&gt;&#xA;&lt;li&gt;The Mythical Man-Month by Frederik P. Brooks Jr.&lt;/li&gt;&#xA;&lt;li&gt;Liar&#39;s Poker by Michael Lewis&lt;/li&gt;&#xA;&lt;li&gt;Zero to One by Peter Thiel&lt;/li&gt;&#xA;&lt;li&gt;In the Plex by Steven Levy&lt;/li&gt;&#xA;&lt;li&gt;Fresh Off the Boat by Eddie Huang&lt;/li&gt;&#xA;&lt;li&gt;Steve Jobs by Walter Isaacson&lt;/li&gt;&#xA;&lt;li&gt;The New New Thing by Michael Lewis&lt;/li&gt;&#xA;&lt;li&gt;The Manager&#39;s Path by Camille Fournier&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Bad Blood&lt;/strong&gt; by John Carreyrou&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A Philosophy of Software Design&lt;/strong&gt; by John Ousterhout&lt;/li&gt;&#xA;&lt;li&gt;Masters of Doom by David Kushner&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Stoner&lt;/strong&gt; by John Williams (only fiction book this year)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I got a Kindle for Christmas, and I&#39;m loving it so far. Looking forward to reading more this year!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Enforcing keyword arguments in Python 3</title>
      <link>https://vegardstikbakke.com/python-keyword-only/</link>
      <pubDate>Tue, 27 Nov 2018 00:00:00 +0000</pubDate>
      <guid>https://vegardstikbakke.com/python-keyword-only/</guid>
      <description>&lt;p&gt;Today I learned that in Python 3, one can enforce the use of keyword arguments in functions!&lt;/p&gt;&#xA;&lt;p&gt;Consider a function for doing safe division. Several things can go wrong when doing division directly, and therefore we might want to use a function which could handle possible errors. One possible error is dividing by zero. This is an undefined operation, and it will raise an error. Another is that in Python, division always returns a floating point number, and Python does not support arbitrarily large floating point numbers. Thus one might get an overflow error. (See &lt;a href=&#34;https://stackoverflow.com/questions/27946595/how-to-manage-division-of-huge-numbers-in-python&#34;&gt;here&lt;/a&gt;.) We might want to introduce two flags for handling these types of errors in our division function, &lt;code&gt;ignore_overflow&lt;/code&gt; and &lt;code&gt;ignore_zero_division&lt;/code&gt;. By default, these should be &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;safe_division&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;divisor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_overflow&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_zero_division&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;number&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;divisor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;OverflowError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_overflow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ZeroDivisionError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_zero_division&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;inf&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Calling this straightforwardly like &lt;code&gt;safe_division(1, 0, True, False)&lt;/code&gt; is not very readable. We would have to check the actual function body to see what this function call would actually do. We can call it like&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;safe_division&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_overflow&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_zero_division&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;or even&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;safe_division&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_zero_division&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_overflow&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(notice how we swapped the order here). However, specifying the keywords is only optional behavior for those calling the function.&lt;/p&gt;&#xA;&lt;p&gt;A solution for this is to enforce using keyword arguments. This is possible to do in Python 2 by specifying a keyword argument dictionary, typically called &lt;code&gt;**kwargs&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;However, in Python 3, there is a simple way to enforce it! By adding a &lt;code&gt;*&lt;/code&gt; in the function arguments, we force all &lt;em&gt;succeeding&lt;/em&gt; arguments to be named. E.g. by having the first argument be &lt;code&gt;*&lt;/code&gt;, we force all arguments to be named.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;safe_division&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;divisor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_overflow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_zero_division&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;number&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;divisor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;OverflowError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_overflow&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;except&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ZeroDivisionError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ignore_zero_division&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;inf&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we now call &lt;code&gt;safe_division(1, 0, True, False)&lt;/code&gt;, or even &lt;code&gt;safe_division(1, 0)&lt;/code&gt; we would get an error:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-python-traceback&#34;&gt;&amp;gt;&amp;gt;&amp;gt; safe_division(1, 2, True, False)&#xA;Traceback (most recent call last):&#xA;  File &amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;, line 1, in &amp;lt;module&amp;gt;&#xA;TypeError: safe_division() takes 0 positional arguments but 4 were given&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;We now have to use it like this:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-python-traceback&#34;&gt;&amp;gt;&amp;gt;&amp;gt; safe_division(number=10**1000, divisor=3**-100,&#xA;ignore_overflow=True, ignore_zero_division=False)&#xA;0&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This makes writing &lt;a href=&#34;https://en.wikipedia.org/wiki/Defensive_programming&#34;&gt;defensive&lt;/a&gt; Python easier!&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>