<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ryan Greenberg: TIL</title><description>Today I Learned</description><link>https://ryangreenberg.com/til/</link><item><title>Show the application switcher on all screens</title><link>https://ryangreenberg.com/til/app-switcher-all-screens/</link><guid isPermaLink="true">https://ryangreenberg.com/til/app-switcher-all-screens/</guid><pubDate>Thu, 20 Aug 2026 17:08:27 GMT</pubDate><content:encoded>&lt;p&gt;I have multiple monitors and I use the &lt;code&gt;cmd + tab&lt;/code&gt; application switcher often. I find the current behavior of macOS pretty confusing. By default, it only shows the switcher on the most recently used screen. But that’s not the screen I’m looking at when switching to another application!&lt;/p&gt;
&lt;p&gt;After getting confused a bunch of times, I found this setting which lets you show the switcher on all screens:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;defaults&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; write&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; com.apple.dock&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; appswitcher-all-displays&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; -bool&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;killall&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; Dock&lt;/span&gt;&lt;span style=&quot;color:#676E95;font-style:italic&quot;&gt; # you need to restart the Dock for settings to take effect&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;figure&quot;&gt;&lt;a href=&quot;https://ryangreenberg.com/_astro/app-switcher-all-screens.Bjwx4MtU.webp&quot;&gt;&lt;img src=&quot;https://ryangreenberg.com/_astro/app-switcher-all-screens.Bjwx4MtU_1knNqG.webp&quot; alt=&quot;Screenshot of two screens with the same App Switcher active on each&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;692&quot; height=&quot;779&quot;&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;It’s wonderful.&lt;/p&gt;</content:encoded></item><item><title>Control `less` options globally with `LESS`</title><link>https://ryangreenberg.com/til/less-env-var/</link><guid isPermaLink="true">https://ryangreenberg.com/til/less-env-var/</guid><pubDate>Mon, 17 Aug 2026 14:07:30 GMT</pubDate><content:encoded>&lt;p&gt;While researching &lt;a href=&quot;https://ryangreenberg.com/til/less-toggle-flags/&quot;&gt;interactive flags in &lt;code&gt;less&lt;/code&gt;&lt;/a&gt; I learned about the &lt;code&gt;LESS&lt;/code&gt; env var. This lets you set &lt;code&gt;less&lt;/code&gt;’s startup flags globally. For example, if you always want ANSI color escape sequences to be interpreted (so you see colors instead of control characters), put &lt;code&gt;LESS=-R&lt;/code&gt; in your shell config.&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export LESS=-R&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;less request.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of HTTP request log with HTTP status codes in color.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1579&quot; height=&quot;1110&quot; src=&quot;https://ryangreenberg.com/_astro/less-dash-r.DLwPZ6-k_xKdtc.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;export LESS=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;less request.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of HTTP request log with HTTP status codes surrounded by control characters like ESC[33m.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1579&quot; height=&quot;1110&quot; src=&quot;https://ryangreenberg.com/_astro/less-without-dash-r.CHDKiba7_Z2cBSmT.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
</content:encoded></item><item><title>`less` lets you toggle startup flags interactively</title><link>https://ryangreenberg.com/til/less-toggle-flags/</link><guid isPermaLink="true">https://ryangreenberg.com/til/less-toggle-flags/</guid><pubDate>Mon, 10 Aug 2026 13:00:30 GMT</pubDate><content:encoded>&lt;p&gt;I love the &lt;code&gt;less&lt;/code&gt; pager. You can use it to read, search, tail, and more. As with many versatile programs, however, the man page for &lt;code&gt;less&lt;/code&gt; has a bazillion different options:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ man less&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;NAME&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    less - opposite of more&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SYNOPSIS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    less [-[+]aABcCdeEfFgGiIJKLmMnNqQrRsSuUVwWX~]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s hardly a letter it doesn’t use!&lt;/p&gt;
&lt;p&gt;Of these, my favorites are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-S or --chop-long-lines&lt;/code&gt;. Truncate lines rather than wrapping them.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-N or --LINE-NUMBERS&lt;/code&gt;. Show line numbers at the start of each line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-R or --RAW-CONTROL-CHARS&lt;/code&gt;. Show control characters vs. interpret them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can supply these as flags when starting &lt;code&gt;less&lt;/code&gt;. But you often don’t start &lt;code&gt;less&lt;/code&gt; directly; it’s set as your pager and then gets invoked when you run commands that produce lots of output, like &lt;code&gt;man&lt;/code&gt; or &lt;code&gt;git&lt;/code&gt;. No matter because you can type the startup flags &lt;em&gt;while &lt;code&gt;less&lt;/code&gt; is running&lt;/em&gt; to toggle their behavior!&lt;/p&gt;
&lt;p&gt;If lines are wrapped and you want to truncate them, just type &lt;code&gt;-S&lt;/code&gt;. You’ll see a message at bottom describing the change and then you press enter to accept. Want to see what line number something is on? Type &lt;code&gt;-N&lt;/code&gt;, enter.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screen recording of opening an HTTP request log with less, then toggling escape codes to color, toggling wrapped long lines to be truncated, then enabling line numbers&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;790&quot; height=&quot;560&quot; src=&quot;https://ryangreenberg.com/_astro/less-toggle-flags.BQE2RSxJ_MbKd4.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;As an aside: the dominant pager that preceded &lt;code&gt;less&lt;/code&gt; was &lt;code&gt;more&lt;/code&gt;. Despite the names, &lt;code&gt;less&lt;/code&gt; has more features. On macOS, &lt;code&gt;more&lt;/code&gt; is now &lt;code&gt;less&lt;/code&gt; (or more precisely, &lt;code&gt;/usr/bin/more&lt;/code&gt; and &lt;code&gt;/usr/bin/less&lt;/code&gt; are the same file):&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ls -lih /usr/bin/more /usr/bin/less&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1152921500312523552 -rwxr-xr-x  2 360K Nov 22  2025 /usr/bin/less&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1152921500312523552 -rwxr-xr-x  2 360K Nov 22  2025 /usr/bin/more&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whether you get &lt;code&gt;more&lt;/code&gt;’s impoverished capabilities or &lt;code&gt;less&lt;/code&gt;’s expansive set is determined by the name you use to invoke it.&lt;/p&gt;
</content:encoded></item><item><title>Mojibake</title><link>https://ryangreenberg.com/til/mojibake/</link><guid isPermaLink="true">https://ryangreenberg.com/til/mojibake/</guid><pubDate>Mon, 03 Aug 2026 12:33:07 GMT</pubDate><content:encoded>&lt;p&gt;I learned a new word today: &lt;a href=&quot;https://en.wikipedia.org/wiki/Mojibake&quot;&gt;mojibake&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Some of my old blog posts have random gibberish in them like &lt;code&gt;Ã±&lt;/code&gt; or &lt;code&gt;â€™&lt;/code&gt;. I always assumed this was the result of some character encoding mismatch but I didn’t know exactly what was going on. While I was fixing this issue via find/replace technology, I did some reading to learn more.&lt;/p&gt;
&lt;p&gt;The short version is a mismatch between &lt;a href=&quot;https://en.wikipedia.org/wiki/UTF-8&quot;&gt;UTF-8&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO/IEC_8859-1&quot;&gt;Latin-1&lt;/a&gt; (ISO-8859-1) encodings. As an example, this is how &lt;code&gt;ñ&lt;/code&gt; becomes &lt;code&gt;Ã±&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, when I type &lt;code&gt;ñ&lt;/code&gt; in my CMS it is represented in UTF-8:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;ñ&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;codepoints&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#F78C6C&quot;&gt;241&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;ñ&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;bytes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#F78C6C&quot;&gt;195&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F78C6C&quot;&gt; 177&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In UTF-8, codepoints above 127 are represented with more than one byte. You can see here the codepoint 241 (&lt;code&gt;U+00F1&lt;/code&gt;) corresponds to two bytes, 195 and 177.&lt;/p&gt;
&lt;p&gt;Then, when these bytes get written to the DB, they are &lt;em&gt;not&lt;/em&gt; converted from UTF-8 to Latin-1. We store [195, 177] but believe they are Latin-1. Later, we re-encode these bytes as UTF-8 to convert from not-actually-Latin-1:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;bytes &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;ñ&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;bytes&lt;/span&gt;&lt;span style=&quot;color:#676E95;font-style:italic&quot;&gt;               # =&amp;gt; [195, 177]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;latin1_str &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; bytes&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;pack&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;C*&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;force_encoding&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;ISO-8859-1&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;puts&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; latin1_str&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;encode&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;UTF-8&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#676E95;font-style:italic&quot;&gt; # =&amp;gt; Ã±&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was fixing these bugs in old posts and one AI result said, “Oh yeah, this is a classic mojibake problem.” My first reaction was, “I don’t know man–How ‘classic’ can it be if I’m hearing it for the first time after working with strings and encodings for years?” But then again, you have to learn everything sometime. Maybe it is classic!&lt;/p&gt;
&lt;p&gt;Mojibake or 文字化け is a Japanese word for exactly this kind of garbled text. Before UTF-8, the dominant character encoding in Japan was &lt;a href=&quot;https://en.wikipedia.org/wiki/Shift_JIS&quot;&gt;Shift JIS&lt;/a&gt;, and this kind of character mangling was probably common when text interacted with systems that did not convert encodings properly.&lt;/p&gt;
&lt;p&gt;The “moji” in mojibake (文字) means text and, yes, it’s the same moji found in emoji (絵文字)! I knew that emoji originated in Japan but I did not know that the name was actually Japanese. I assumed that &lt;code&gt;emo&lt;/code&gt; was related to emotion since the first emoji were all faces showing different emotions. Nope! E (絵) means pictures, moji (文字) means text.&lt;/p&gt;
&lt;p&gt;Now that UTF-8 is the dominant text encoding, I’m pretty confident you’ll see all the text here correctly and not as a “classic” mojibake issue.&lt;/p&gt;
</content:encoded></item><item><title>Spotlight can do math and unit conversions</title><link>https://ryangreenberg.com/til/spotlight-math/</link><guid isPermaLink="true">https://ryangreenberg.com/til/spotlight-math/</guid><pubDate>Thu, 30 Jul 2026 11:59:21 GMT</pubDate><content:encoded>&lt;p&gt;You can open macOS’s Spotlight with &lt;code&gt;cmd + space&lt;/code&gt;. Then you can type in math like &lt;code&gt;(4 + 23)  * 92&lt;/code&gt; and it will show you the answer. I’ve been using this feature for awhile but I just learned that it can do a lot more than I realized! This came up because I typed &lt;code&gt;2**256&lt;/code&gt; and it gave me the answer. Interesting…what else can it do?&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of Spotlight answers to math like round(4723/125) and sin(pi/2), as well as unit conversions like 95F and 10 miles in feet. The answer to each query is selected in blue so it can be copy/pasted.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1200&quot; height=&quot;886&quot; src=&quot;https://ryangreenberg.com/_astro/spotlight-math.oxUeFU4p_1jPxtf.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I couldn’t find a comprehensive list of supported features. This &lt;a href=&quot;https://apple.stackexchange.com/a/461875&quot;&gt;Stack Overflow answer&lt;/a&gt; has a long list; one other commenter says it’s everything the macOS calculator can do. All of these things work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exponents, written as any of &lt;code&gt;2**3&lt;/code&gt;, &lt;code&gt;3^2&lt;/code&gt;, or &lt;code&gt;pow(2, 10)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Factorials, written as &lt;code&gt;5!&lt;/code&gt; or &lt;code&gt;fact(7)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Trigonometry, functions like &lt;code&gt;sin&lt;/code&gt;, &lt;code&gt;cos&lt;/code&gt;, &lt;code&gt;tan&lt;/code&gt;, and constants like &lt;code&gt;pi&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Lots of other math. You can &lt;code&gt;round()&lt;/code&gt;, &lt;code&gt;pow()&lt;/code&gt;, &lt;code&gt;abs()&lt;/code&gt;, &lt;code&gt;sqrt()&lt;/code&gt;, &lt;code&gt;ceil()&lt;/code&gt;, &lt;code&gt;floor()&lt;/code&gt;, and more.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The absolutely best part is that it does unit conversions. Historically this is one of the things I have always typed into Google search. I’m sure I’m going to use this all the time now. Type &lt;code&gt;95F&lt;/code&gt; and it tells you that’s 35°C. Type &lt;code&gt;170 lbs&lt;/code&gt; and it says 77 kg. You can specify the output unit like &lt;code&gt;4 Tbsp in mL&lt;/code&gt; or &lt;code&gt;10 miles in feet&lt;/code&gt; (it does not, however, support unhinged unit conversion like “1 mile in furlongs”).&lt;/p&gt;
&lt;p&gt;One really nice UX touch is that Spotlight automatically selects the answer, so you can easily copy it with &lt;code&gt;cmd + C&lt;/code&gt; without dragging.&lt;/p&gt;
</content:encoded></item><item><title>`pkill` to stop a process by name</title><link>https://ryangreenberg.com/til/pkill/</link><guid isPermaLink="true">https://ryangreenberg.com/til/pkill/</guid><pubDate>Mon, 27 Jul 2026 10:03:55 GMT</pubDate><content:encoded>&lt;p&gt;Sometimes when I learn about a tool I think “wow, that would have been useful [when I had some problem in the past]”. That happened with &lt;code&gt;pkill&lt;/code&gt;, which is a command you can use to stop a process accurately.&lt;/p&gt;
&lt;p&gt;Many years ago I was slogging through debugging a Selenium test failure in a Rails application with my coworker, Ron. Every time we made a change we had to stop the server, restart it, and re-run the test suite. This had a bunch of steps so naturally we wrote a script to automate them. At the end of the script, we killed the Rails server by looking for the process using port 3000 with &lt;code&gt;kill -9 $(lsof -t -i:3000)&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lsof&lt;/code&gt; lists open files. In unix/linux, everything is a file including things like network connections (via sockets).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t&lt;/code&gt; terse output–just the pid&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-i:3000&lt;/code&gt; process using port 3000&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It worked! Until one run when, all of a sudden, my entire browser disappeared. It was unexpected, abrupt, and rude. We were using that browser! Ron and I were really baffled … until we realized that there were two processes using the port: the server and the browser (maybe with a keep alive connection?).&lt;/p&gt;
&lt;p&gt;This is what the &lt;code&gt;lsof&lt;/code&gt; output looks like when you look for processes using a port:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ lsof -i:3000 -P&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;COMMAND  PID    TYPE  NODE  NAME&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ruby3.2  42727  IPv6  TCP   *:3000                           (LISTEN)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ruby3.2  42727  IPv6  TCP   localhost:3000-&amp;gt;localhost:57537  (ESTABLISHED)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Google   6439   IPv6  TCP   localhost:57537-&amp;gt;localhost:3000  (ESTABLISHED)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that process ID 42727 is using &lt;code&gt;:3000&lt;/code&gt; in the &lt;code&gt;LISTEN&lt;/code&gt; state, while that same process and Chrome are both using it for a connection in the &lt;code&gt;ESTABLISHED&lt;/code&gt; state. So we got unlucky on one run, grabbed the browser’s process ID, and unceremonously killed it. At the time, we worked around this by adding another flag, &lt;code&gt;-sTCP:LISTEN&lt;/code&gt;. This filters to TCP sockets in the LISTEN state, so we’d always get the server process.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkill&lt;/code&gt; would have been perfect for this! It stops processes by matching the process name. For example, &lt;code&gt;pkill ruby&lt;/code&gt; would stop all running ruby processes. That’s a little aggressive if you only want to stop one process. With &lt;code&gt;pkill -f&lt;/code&gt; you match the program arguments as well, like &lt;code&gt;ruby bin/rails&lt;/code&gt; so &lt;code&gt;pkill -9 -f rails&lt;/code&gt; would do it. (You could also argue that I should have the server process itself write its pid to a file to later stop it. You’re probably right!)&lt;/p&gt;
&lt;p&gt;One thing I love is programs that have two modes to either (1) take action or (2) preview that action. Many systems expose this as a “dry run”. In the case of &lt;code&gt;pkill&lt;/code&gt;, the dry-run equivalent is &lt;code&gt;pgrep&lt;/code&gt;, which works the same way, but just outputs the pid instead of sending a kill signal.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pkill&lt;/code&gt; has a &lt;em&gt;ton&lt;/em&gt; of other options so be sure to look at the &lt;a href=&quot;https://tldr.inbrowser.app/pages/common/pkill&quot;&gt;tldr&lt;/a&gt; or &lt;a href=&quot;https://man.freebsd.org/cgi/man.cgi?query=pkill&amp;amp;sektion=1&amp;amp;format=html&quot;&gt;man page&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>`cmd + shift + G` to Go To Folder</title><link>https://ryangreenberg.com/til/cmd-shift-g-to-go-to-folder/</link><guid isPermaLink="true">https://ryangreenberg.com/til/cmd-shift-g-to-go-to-folder/</guid><pubDate>Tue, 21 Jul 2026 13:30:55 GMT</pubDate><content:encoded>&lt;p&gt;GUIs are great! But I’m used to typing &lt;code&gt;cd&lt;/code&gt;, and sometimes I prefer that to clicking around to get somewhere.&lt;/p&gt;
&lt;p&gt;MacOS merges these worlds with &lt;code&gt;cmd + shift + G&lt;/code&gt;. In the Finder it’s under &lt;code&gt;Go&lt;/code&gt; → &lt;code&gt;Go to Folder...&lt;/code&gt;. This opens a dialog where you can type a path to navigate to. It supports tab completion, it understands that &lt;code&gt;~&lt;/code&gt; is your home directory, and it shows recently used options that you can navigate with your arrow keys.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of Go to Folder window with ~/code/ in the input box and a list of possible destinations below&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;920&quot; height=&quot;360&quot; src=&quot;https://ryangreenberg.com/_astro/go-to-folder-in-finder.-47FQjaq_Z1VK4kQ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It also works in the open/save dialog boxes! This is more hidden; as far as I know there is no menu that shows “Go To Folder” so you just have to know the &lt;code&gt;cmd + shift + G&lt;/code&gt; shortcut.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of Go to Folder being used with an open/save dialog box in the background&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1645&quot; height=&quot;936&quot; src=&quot;https://ryangreenberg.com/_astro/go-to-folder-in-open-save-dialog.BMv5SxGH_Z27Y6JW.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now when I’m not navigating to the Desktop, Documents, or Downloads (which each have their own keyboard shortcuts), I often use &lt;code&gt;cmd + shift + G&lt;/code&gt; to get there.&lt;/p&gt;
</content:encoded></item><item><title>badssl.com lets you test bad certificates</title><link>https://ryangreenberg.com/til/badssl/</link><guid isPermaLink="true">https://ryangreenberg.com/til/badssl/</guid><pubDate>Fri, 17 Jul 2026 12:28:04 GMT</pubDate><content:encoded>&lt;p&gt;While working on Slack’s unfurl previews I needed to test what happens when the fetcher cannot access a site. People put a lot of different URLs into Slack; some of them are going to be unfetchable for various reasons. Pointing the fetcher at a URL that doesn’t resolve, or one that results in a timeout, is straightforward, but I don’t know enough to set up various bad SSL/TLS scenarios. Surely one of them is going to have a bad config at some point.&lt;/p&gt;
&lt;p&gt;That’s when I found &lt;a href=&quot;https://badssl.com&quot;&gt;https://badssl.com&lt;/a&gt;. It’s a &lt;a href=&quot;https://github.com/chromium/badssl.com&quot;&gt;project&lt;/a&gt; from some security folks that provides tons of misconfigured sites for exactly this purpose. Want to see what happens with an expired cert? Go to &lt;a href=&quot;https://expired.badssl.com&quot;&gt;https://expired.badssl.com&lt;/a&gt;. How about a cert that’s been revoked? That’s &lt;a href=&quot;https://revoked.badssl.com/&quot;&gt;https://revoked.badssl.com/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, this is how &lt;code&gt;curl&lt;/code&gt; behaves when the certificate doesn’t match the domain name:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ curl https://wrong.host.badssl.com/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;curl: (60) SSL: no alternative certificate subject name matches target host name &amp;#39;wrong.host.badssl.com&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;More details here: https://curl.se/docs/sslcerts.html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;curl failed to verify the legitimacy of the server and therefore could not&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;establish a secure connection to it. To learn more about this situation and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;how to fix it, please visit the web page mentioned above.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what it looks like when you navigate to a site where the certificate has expired:&lt;/p&gt;
&lt;figure class=&quot;figure&quot;&gt;&lt;a href=&quot;https://ryangreenberg.com/_astro/expired-cert-in-chrome.Bpvilwng.png&quot;&gt;&lt;img src=&quot;https://ryangreenberg.com/_astro/expired-cert-in-chrome.Bpvilwng_29e4px.webp&quot; alt=&quot;Screenshot of Chrome showing a warning for expired.badssl.com that says &amp;quot;Your connection is not private&amp;quot; and the error code ERR_CERT_DATE_INVALID.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;692&quot; height=&quot;490&quot;&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;I love that &lt;a href=&quot;https://badssl.com&quot;&gt;badssl.com&lt;/a&gt; gives you a quick way to check these behaviors interactively. For example, &lt;code&gt;curl https://tls-v1-1.badssl.com:1011&lt;/code&gt; works fine; going to that URL in Chrome presents a warning (because TLS v1.1 &lt;a href=&quot;https://datatracker.ietf.org/doc/rfc8996/&quot;&gt;is deprecated&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;There are more than 70 subdomains in 13 categories so you can trigger mixed content warnings, HTTP form behaviors, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security&quot;&gt;HSTS&lt;/a&gt;, and more.&lt;/p&gt;</content:encoded></item><item><title>Find shared lines with `comm`</title><link>https://ryangreenberg.com/til/comm/</link><guid isPermaLink="true">https://ryangreenberg.com/til/comm/</guid><pubDate>Tue, 14 Jul 2026 15:24:56 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;diff&lt;/code&gt; is a well-known tool to see what lines are different between files; &lt;code&gt;comm&lt;/code&gt; is a less-known tool to see the common lines.&lt;/p&gt;
&lt;p&gt;I found &lt;code&gt;comm&lt;/code&gt; when I was looking for a way to identify lines that are shared (or not) between files. Specifically, I had two lists: one with all the APIs in my codebase, and another with all the tested APIs. From this I wanted to get a list of &lt;em&gt;untested&lt;/em&gt; APIs.&lt;/p&gt;
&lt;p&gt;As an aside, my go-to in this situation is to drop in a Ruby one-ish-liner:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;ruby&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; -e&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;puts (File.readlines(&apos;all&apos;) - File.readlines(&apos;tested&apos;))&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Ruby, &lt;a href=&quot;https://ruby-doc.org/current/Array.html#method-i-2D&quot;&gt;&lt;code&gt;Array.-&lt;/code&gt;&lt;/a&gt; (minus) returns a new array after removing the elements from the right-hand side. IMO, this is pretty nice. Ruby has a wide variety of methods for &lt;a href=&quot;https://ruby-doc.org/current/Array.html#class-Array-label-Methods+for+Combining&quot;&gt;combining arrays&lt;/a&gt; (&lt;code&gt;+&lt;/code&gt; to merge, &lt;code&gt;-&lt;/code&gt; for difference, &lt;code&gt;&amp;amp;&lt;/code&gt; for intersection, &lt;code&gt;|&lt;/code&gt; for union). The only caveat is that this is trickier if you haven’t written the lines to a file. That’s pretty common in pipelines and was, in fact, exactly the situation in which I found myself.&lt;/p&gt;
&lt;p&gt;Let’s say we have these two lists: &lt;code&gt;mammals.txt&lt;/code&gt; and &lt;code&gt;pets.txt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ cat mammals.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;lion&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;rabbit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;whale&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ cat pets.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fish&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;rabbit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;snake&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can compare the files with &lt;code&gt;comm&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ comm mammals.txt pets.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		dog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	fish&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;lion&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		rabbit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;	snake&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;whale&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first time I saw this output I was pretty confused. Is this working? It kind of looks like I just dumped all the lines with weird indentation? &lt;code&gt;comm&lt;/code&gt; produces output in three columns:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;lines that are only in the first file&lt;/li&gt;
&lt;li&gt;lines that are only in the second file&lt;/li&gt;
&lt;li&gt;lines that are in both files&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here’s a labeled version:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ comm mammals.txt pets.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;just          | just       |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mammals.txt   | pets.txt   | both&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;--------------+------------+-------&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              |            | cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              |            | dog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              | fish       |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;lion          |            |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              |            | rabbit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              | snake      |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;whale         |            |&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use &lt;code&gt;-1&lt;/code&gt; to hide the first column, &lt;code&gt;-2&lt;/code&gt; to hide the second column, and, yes, &lt;code&gt;-3&lt;/code&gt; to hide the third column. In this case, if you want just animals that are mammals and pets: &lt;code&gt;comm -12 mammals.txt pets.txt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ comm -12 mammals.txt pets.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;rabbit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want pets that &lt;em&gt;aren’t&lt;/em&gt; mammals, that would be &lt;code&gt;comm -13 mammals.txt pets.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To get back to my original use case, if I want a list of &lt;em&gt;untested&lt;/em&gt; APIs, you write:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;comm -23 all_apis.txt tested_apis.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;comm&lt;/code&gt; works with process substitution so you can also write:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;comm -23 &amp;lt;(command to find all APIs) &amp;lt;(command to find tested APIs)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One caveat: the input lines need to be sorted, so you may need to add &lt;code&gt;| sort&lt;/code&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Reorder columns, print the last with `awk`</title><link>https://ryangreenberg.com/til/columns-with-awk/</link><guid isPermaLink="true">https://ryangreenberg.com/til/columns-with-awk/</guid><pubDate>Thu, 09 Jul 2026 13:25:43 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;awk&lt;/code&gt; is a whole programming language, but 90% of what I use it for is printing a subset of columns. This is pretty much covered by &lt;code&gt;cut&lt;/code&gt; with two exceptions: re-ordering columns and printing the last column. Both &lt;code&gt;cut&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt; call columns “fields”.&lt;/p&gt;
&lt;p&gt;Here’s some sample tab-separated data (formatted with &lt;a href=&quot;https://ryangreenberg.com/tils/column-t&quot;&gt;column -t&lt;/a&gt;!):&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FirstName  LastName  FavoriteColor  Pet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Elena      Kowalski  yellow         hamster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Marcus     Rivera    green          snake&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Nadia      Ferreira  coral          rabbit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Yuki       Tanaka    pink           cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Zara       Okonkwo   indigo         chinchilla&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you just want a list of favorite colors, you select the third field with &lt;code&gt;cut -f3&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; data.tsv&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt; cut&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; -f3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;FavoriteColor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;yellow&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;green&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;coral&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;pink&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;indigo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if you want last name then first name, no dice. You can run &lt;code&gt;cut -f2,1&lt;/code&gt; but it will output them in the original order. For this I learned to use awk. You have to remember to specify the field separator with &lt;code&gt;-F&lt;/code&gt;, then you put the program to run on each line in braces:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; data.tsv&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt; awk&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; -F&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;\t&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;{ print $2, $1 }&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;LastName&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; FirstName&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;Kowalski&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; Elena&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;Rivera&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; Marcus&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;Ferreira&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; Nadia&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;Tanaka&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; Yuki&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;Okonkwo&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; Zara&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want the &lt;em&gt;last&lt;/em&gt; field in the input (in my case I was looking at log lines), you have two options: count the number of fields and use &lt;code&gt;cut&lt;/code&gt; (no thanks, I’m too lazy) or use &lt;code&gt;awk&lt;/code&gt;. The variable you want is &lt;code&gt;$NF&lt;/code&gt; for “number of fields”. Since the fields are one-indexed, this will give you the last field.&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; data.tsv&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt; awk&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; -F&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;\t&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;{ print $NF }&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;Pet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;hamster&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;snake&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;rabbit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;chinchilla&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I usually have to look up &lt;code&gt;-F&apos;\t&apos;&lt;/code&gt; even though I should probably remember “field separator” is &lt;code&gt;-F&lt;/code&gt;.&lt;/p&gt;
</content:encoded></item><item><title>`Number.MAX_SAFE_INTEGER` is 2**53 - 1</title><link>https://ryangreenberg.com/til/max-safe-integer/</link><guid isPermaLink="true">https://ryangreenberg.com/til/max-safe-integer/</guid><pubDate>Fri, 03 Jul 2026 12:39:42 GMT</pubDate><content:encoded>&lt;p&gt;I was filling out a form today and a friend joked, “You should put &lt;code&gt;Number.MAX_SAFE_INTEGER&lt;/code&gt; in the budget field.” Woah! I didn’t know we had that constant.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER&quot;&gt;&lt;code&gt;Number.MAX_SAFE_INTEGER&lt;/code&gt;&lt;/a&gt; was added to JavaScript in 2015 as part of ES6. The constant is new to me, but the value is a number I know by heart: 2**53 - 1.&lt;/p&gt;
&lt;p&gt;When I worked at Twitter, we switched from 32-bit IDs generated by MySQL to 64-bit IDs generated by &lt;a href=&quot;https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake&quot;&gt;Snowflake&lt;/a&gt;. Late in the project–just a few weeks before launch–we hit a small road bump: we could not use the new IDs in the browser.&lt;/p&gt;
&lt;p&gt;With the Snowflake IDs, sometimes you would like a tweet and the request would fail, “tweet not found”. You would scroll the timeline and duplicate tweets would appear. These bugs and others were caused by the way JavaScript represents numbers.&lt;/p&gt;
&lt;p&gt;JavaScript implents &lt;a href=&quot;https://en.wikipedia.org/wiki/IEEE_754&quot;&gt;IEEE-754&lt;/a&gt;, which uses 64 bits to represent all its numbers–big, small, floating point, integer. Practically this means that you do not get 64 bits to represent integers. Of the 64 bits, 1 bit represents the sign, 11 bits are used to represent the exponent of a number, and the remaining 52 bits are what’s left for the mantissa (what you typically think of as the actual number). This means you can only represent integers faithfully up to 2**53 - 1, &lt;code&gt;Number.MAX_SAFE_INTEGER&lt;/code&gt;, &lt;code&gt;9007199254740991&lt;/code&gt;. (This was 2010 and support for bigints didn’t land until 2018-2020.)&lt;/p&gt;
&lt;div class=&quot;photo highlight&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Double-precision_floating-point_format&quot;&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;960&quot; height=&quot;195&quot; src=&quot;https://ryangreenberg.com/_astro/ieee_754_double_floating_point_format.CCbyJnzE_ZlSnue.webp&quot; srcset=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em class=&quot;photo-caption&quot;&gt;The memory format of an IEEE 754 double-precision floating-point value. Illustration &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:IEEE_754_Double_Floating_Point_Format.svg&quot;&gt;from Wikipedia&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Above that limit, the numbers behave in unexpected ways:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt; Number.MAX_SAFE_INTEGER&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9007199254740991&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt; Number.MAX_SAFE_INTEGER + 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9007199254740992&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt; Number.MAX_SAFE_INTEGER + 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9007199254740992&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt; Number.MAX_SAFE_INTEGER + 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9007199254740994&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tweets in the API came back like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#C792EA&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#F78C6C&quot;&gt; 915710611442491393&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#C792EA&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;but the browser would parse the id as &lt;code&gt;915710611442491400&lt;/code&gt;. That’s close! But close isn’t usually what you want for IDs.&lt;/p&gt;
&lt;p&gt;At the time, we solved the problem by adding &lt;code&gt;&quot;id_str&quot;&lt;/code&gt; with a string representation of the ID. We couldn’t change or remove &lt;code&gt;&quot;id&quot;&lt;/code&gt; because it was a widely used public API, but that vestigial field was a perennial cactus underfoot. People would do things like use the field, and get unexpected results when the ID was not the ID.&lt;/p&gt;
&lt;p&gt;See also the &lt;a href=&quot;https://github.com/twitter-archive/snowflake/tree/snowflake-2010&quot;&gt;archived source for snowflake&lt;/a&gt; and the archived &lt;a href=&quot;https://web.archive.org/web/20101121003124/https://www.twitpocalypse.com/&quot;&gt;twitpocalypse.com&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>`Pathname` for file operations</title><link>https://ryangreenberg.com/til/pathname-for-file-operations/</link><guid isPermaLink="true">https://ryangreenberg.com/til/pathname-for-file-operations/</guid><pubDate>Wed, 01 Jul 2026 12:35:16 GMT</pubDate><content:encoded>&lt;p&gt;I used Ruby extensively at Twitter and it remains one of my favorite languages, especially for tools and scripts. That said, my knowledge of production Ruby is largely from the 1.8.7 / 1.9.3 era, which is &lt;a href=&quot;https://www.ruby-lang.org/en/downloads/releases/&quot;&gt;quite old&lt;/a&gt;. We’re now on Ruby 4.0.&lt;/p&gt;
&lt;p&gt;I still write a lot of scripts and these days I ask Claude to write many of them for me. I noticed it wrote some stuff using the &lt;a href=&quot;https://docs.ruby-lang.org/en/4.0/Pathname.html&quot;&gt;Pathname&lt;/a&gt; module. I assumed it was a newer addition to the language, but it’s extremely old–it was &lt;a href=&quot;https://github.com/ruby/ruby/commit/326569b7dffb805838768d985b6a2c78dfa046c1&quot;&gt;created in 2003&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Pathname&lt;/code&gt; wraps a path and provides an OO facade for lots of common file operations from different modules like &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;Dir&lt;/code&gt;, and &lt;code&gt;FileUtils&lt;/code&gt;. Without &lt;code&gt;Pathname&lt;/code&gt;, you pass the path around and call methods:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rb&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;dir &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;./config&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt; File&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;dir&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;app.json&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;exist?&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;read&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;new content&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;directory?&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;f &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt; File&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;open&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;Pathname&lt;/code&gt;, you wrap the path and then can call methods on it directly:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rb&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;dir &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt; Pathname&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#F78C6C&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;./config&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; dir &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;app.json&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;exist?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;write&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;new content&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;directory?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;f &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;open&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The facade for methods that typically return string paths wraps them as &lt;code&gt;Pathname&lt;/code&gt; so it’s easy to chain:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;rb&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;dir &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;./config&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;Dir&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;glob&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;join&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;dir&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;*.json&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)).&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;each&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; {|&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt; File&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;read&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;dir &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt; Pathname&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#F78C6C&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;./config&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;dir&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;glob&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;*.json&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;each&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; {|&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt; p&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;read&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wish I had known about &lt;code&gt;Pathname&lt;/code&gt; sooner because I definitely would have used it more. Especially in Ruby, which emphasizes everything-is-an-object, it’s a nice improvement to my previous handling of paths.&lt;/p&gt;
</content:encoded></item><item><title>8b/10b encoding</title><link>https://ryangreenberg.com/til/8b-10b-encoding/</link><guid isPermaLink="true">https://ryangreenberg.com/til/8b-10b-encoding/</guid><pubDate>Mon, 29 Jun 2026 18:22:29 GMT</pubDate><content:encoded>&lt;p&gt;Programmers are forever coming up with clever ways to represent data more compactly. Like storing the entire English dictionary using a &lt;a href=&quot;https://en.wikipedia.org/wiki/Trie&quot;&gt;trie&lt;/a&gt;; or compressing fax transmissions 10x using &lt;a href=&quot;https://en.wikipedia.org/wiki/Run-length_encoding&quot;&gt;run-length encoding&lt;/a&gt;; or storing thousands of values in a few kilobytes using &lt;a href=&quot;https://en.wikipedia.org/wiki/Bloom_filter&quot;&gt;probabilistic data structures&lt;/a&gt;. I might not come up with one of these schemes myself but when I read how they’re implemented I’m usually able to come away with an understanding of how they work, even if they feel a bit magical when you first hear about them.&lt;/p&gt;
&lt;p&gt;I was on a random Wikipedia walk when I stumbled on &lt;a href=&quot;https://en.wikipedia.org/wiki/8b/10b_encoding&quot;&gt;8 bit/10 bit encoding&lt;/a&gt;. My first reaction was complete bafflement. In what world can you represent 10 bits with just 8 bits? Are you tapping into some hidden quantum realm or something? But actually it’s the opposite: the encoding uses 10 bits to represent 8 bits of data. If anything that’s &lt;em&gt;more&lt;/em&gt; interesting. Of course you can do it–no one wonders how you can fit 8 numbers into 10 slots–but why?&lt;/p&gt;
&lt;p&gt;The answer is a reminder that I don’t really know much about the physical world of computers. It largely boils down to the fact that it’s problematic to transmit lots of 1s or 0s in a row. But sometimes we’re going to do that! If someone writes an email that has a bunch of consecutive &lt;code&gt;@&lt;/code&gt; signs (ASCII 64, &lt;code&gt;01000000&lt;/code&gt;), then we’re going to send tons of zeros over the wire. The issue with lots of 1s and 0s is that it causes problems with the voltage in the line, and problems with clock synchronization.&lt;/p&gt;
&lt;p&gt;The voltage drift is called &lt;a href=&quot;https://en.wikipedia.org/wiki/DC_bias&quot;&gt;DC bias&lt;/a&gt;. Even after reading the article on it, I was a little lost.&lt;/p&gt;
&lt;p&gt;The clock problem is easier for me to understand. When you’re receiving a message, you use a clock to decide when to sample the voltage to see if it’s high (1) or low (0). I imagine it like a metronome and on each tick you read a bit. But, of course, clocks are hard! It’s easy for them to drift and get out of sync. So you use transitions between 0 and 1 to re-synchronize (see &lt;a href=&quot;https://en.wikipedia.org/wiki/Clock_recovery&quot;&gt;“Clock Recovery”&lt;/a&gt;). If one of those transitions happens before your metronome ticks, you know that you’re running a little behind and can adjust. If you don’t have those transitions (because lots of 0s are being transmitted), then your clock can drift. When that happens the data is going to be wrong because you’re reading the voltage at the wrong time.&lt;/p&gt;
&lt;p&gt;One solution to the problem of too many 1s or 0s is 8b/10b encoding. Because you have extra bits, you can use them to create multiple representations of the underlying bytes. Some of them are already balanced enough and you can send them as-is. For others you create different representations then you alternate between them to achieve balance between the number of 1s and 0s transmitted. In fact, 8b/10b makes that guarantee: if you send at least 20 bits, you will have practically the same number of 1s and 0s.&lt;/p&gt;
&lt;p&gt;8b/10b encoding is used for some forms of Gigabit ethernet, USB 3.0, DisplayPort, and a variety of other protocols. Other systems use related line codes for similar reasons: Fast Ethernet (100BaseT) uses &lt;a href=&quot;https://en.wikipedia.org/wiki/4B5B&quot;&gt;4B5B&lt;/a&gt;; 10 Gbps Ethernet uses &lt;a href=&quot;https://en.wikipedia.org/wiki/64b/66b_encoding&quot;&gt;64b/66b&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I enjoy living in software land where I don’t spend a lot of time thinking about these problems but it was fascinating to learn about.&lt;/p&gt;
</content:encoded></item><item><title>`brew bundle` and Homebrew Casks replaced my bespoke scripts</title><link>https://ryangreenberg.com/til/brew-bundle-casks/</link><guid isPermaLink="true">https://ryangreenberg.com/til/brew-bundle-casks/</guid><pubDate>Fri, 26 Jun 2026 15:15:55 GMT</pubDate><content:encoded>&lt;p&gt;Several years ago I tried to automate my new computer setup so that I could configure a new work laptop or home computer the way I like it easily. It went something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Put a list of Homebrew formula in a file.&lt;/li&gt;
&lt;li&gt;Write a script to loop over those and run &lt;code&gt;brew install&lt;/code&gt; for each of them.&lt;/li&gt;
&lt;li&gt;Keep a list of apps like 1Password, Sublime Text, VS Code. Download and install those.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last time I revisited my setup, I found that almost everything I wrote could be replaced with built-in stuff (great!).&lt;/p&gt;
&lt;p&gt;Homebrew added support for installing a bunch of formulas from a &lt;a href=&quot;https://docs.brew.sh/Brew-Bundle-and-Brewfile&quot;&gt;Brewfile&lt;/a&gt; by running &lt;code&gt;brew bundle install&lt;/code&gt;. You can bootstrap a Brewfile from existing stuff you have installed with &lt;code&gt;brew bundle dump&lt;/code&gt;. You can have multiple files–I keep a base Brewfile, and separate ones for work-only and home-only formulas in my dotfiles repo.&lt;/p&gt;
&lt;p&gt;Homebrew also added support for &lt;a href=&quot;https://github.com/Homebrew/homebrew-cask&quot;&gt;Casks&lt;/a&gt;, which are packages for pre-compiled artifacts like applications and fonts. Now instead of searching for some program, downloading it, running the installer (or dragging to Applications), unmounting, and trashing the disk image, you can simply run &lt;code&gt;brew install 1password&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Neither of these things are super new! I might even have been able to avoid writing my own bootstrapping scripts in the first place if I was paying attention. Support for bundles was &lt;a href=&quot;https://github.com/Homebrew/legacy-homebrew/pull/24107&quot;&gt;added in 2013&lt;/a&gt;. Casks started &lt;a href=&quot;https://github.com/Homebrew/homebrew-cask/commit/5a0d1d5556e3f963a0d34da46e16ecffa59ea2fc&quot;&gt;in 2012&lt;/a&gt; and eventually migrated to the main toolchain &lt;a href=&quot;https://github.com/Homebrew/brew/commit/e81f4ab7de&quot;&gt;in 2016&lt;/a&gt;. Over time, Brewfiles have gained support for a &lt;em&gt;ton&lt;/em&gt; of other things: casks, installation from the Mac App Store, VS Code extensions, go tools, python tools, and more.&lt;/p&gt;
&lt;p&gt;My current Brewfile looks like this (edited for length):&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Core formula&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;brew &quot;fzf&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;brew &quot;gh&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;brew &quot;git&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;brew &quot;jq&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;brew &quot;ripgrep&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Apps&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cask &quot;1password&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cask &quot;google-chrome&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cask &quot;rectangle&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cask &quot;sublime-text&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cask &quot;visual-studio-code&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Apps from the Mac App Store&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mas &quot;Pixelmator Pro&quot;, id: 1289583905&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# VS Code extensions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;vscode &quot;braver.vscode-solarized&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;vscode &quot;golang.go&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;vscode &quot;shopify.ruby-lsp&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I deleted a bunch of code from my bootstrapping process. Now when I want to download and install an app, I start with &lt;code&gt;brew search [name]&lt;/code&gt; and install via Homebrew so I can track install, uninstall more easily.&lt;/p&gt;
</content:encoded></item><item><title>Use process substitution to tag stdout and stderr</title><link>https://ryangreenberg.com/til/process-substitution-tag-stdout-stderr/</link><guid isPermaLink="true">https://ryangreenberg.com/til/process-substitution-tag-stdout-stderr/</guid><pubDate>Wed, 24 Jun 2026 15:07:54 GMT</pubDate><content:encoded>&lt;p&gt;I learned something new about shells today! You can use &lt;code&gt;&amp;gt;(...)&lt;/code&gt; to write output to another process.&lt;/p&gt;
&lt;p&gt;You are almost certainly familiar with command substitution: &lt;code&gt;ls -l $(which git)&lt;/code&gt; runs &lt;code&gt;which git&lt;/code&gt; first and substitutes that as an argument to &lt;code&gt;ls -l&lt;/code&gt; (you can also use backticks but they can’t be nested like &lt;code&gt;$(..)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;You might know about &lt;code&gt;&amp;lt;(...)&lt;/code&gt; which lets you turn the output of a command into a pseudo file. This is &lt;a href=&quot;https://en.wikipedia.org/wiki/Process_substitution&quot;&gt;&lt;em&gt;process&lt;/em&gt; substitution&lt;/a&gt;. It’s helpful for commands that only operate on files. For example &lt;code&gt;diff&lt;/code&gt;, which compares two files:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;diff&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &amp;lt;(&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;abc\ndef\n&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;)&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &amp;lt;(&lt;/span&gt;&lt;span style=&quot;color:#82AAFF&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;abc\n123\ndef\n&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;1a2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 123&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The way this works is that the output of &lt;code&gt;&amp;lt;(cmd)&lt;/code&gt; becomes a file descriptor:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ ls -l &amp;lt;(echo &quot;abc&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prw-rw---- 0 ryan staff 8 Jan 28 11:21 /dev/fd/11&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;p&lt;/code&gt; that you see in the ls output indicates that this is a pipe or “FIFO”; on Linux you’ll see slightly different output. Not all commands play nicely with these fds, zsh provides &lt;code&gt;=(...)&lt;/code&gt; which writes the output to an actual temporary file:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ls -l =(echo &quot;abc&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-rw------- 1 ryan wheel 8 Jan 28 11:22 /tmp/zshAnZjWQ&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wrote all of this up for the #cli-pro-tips channel I created at Slack many years ago (and rewrote it now because I did not put the content I wrote for that channel anywhere else–I wish I had started recording TILs long ago!).&lt;/p&gt;
&lt;p&gt;Today I learned there is an entirely new way to compose this: &lt;code&gt;&amp;gt;(...)&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;(command)   # read from command&apos;s stdout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;(command)   # write to command&apos;s stdin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Specifically, I was wondering how I could tag the output of a command as going to stderr vs stdout without writing something that wraps the whole command. It turns out it’s easy with this technique.&lt;/p&gt;
&lt;p&gt;Here’s a Ruby command that writes even numbers to stdout, odd numbers to stderr. By default, you can’t tell what is going where:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;ruby&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; -e&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;10.times {|i| (i.even? ? STDOUT : STDERR).puts i}&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;9&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can redirect stdout and stderr to a program that will read the input and append the source.&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;ruby&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; -e&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;$stdout.sync = true; 10.times {|i| (i.even? ? STDOUT : STDERR).puts i}&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &amp;gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;sed&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; $&apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;s/^/[STDOUT] /&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;)&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; \     &lt;/span&gt;&lt;span style=&quot;color:#676E95;font-style:italic&quot;&gt;# write stdout to one sed program&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;  2&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;sed&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; $&apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;s/^/[STDERR] /&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &amp;gt;&amp;amp;2)&lt;/span&gt;&lt;span style=&quot;color:#676E95;font-style:italic&quot;&gt;  # write stderr to another&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDOUT&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDERR&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDOUT&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDERR&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDERR&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDOUT&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDERR&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDOUT&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDOUT&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;STDERR&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; 9&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;&amp;gt; &amp;gt;(sed $&apos;s/^/[STDOUT] /&apos;)&lt;/code&gt; redirects stdout to one sed program, and
&lt;code&gt;2&amp;gt; &amp;gt;(sed $&apos;s/^/[STDERR] /&apos; &amp;gt;&amp;amp;2)&lt;/code&gt; redirects stderr to another.&lt;/p&gt;
&lt;p&gt;(I added &lt;code&gt;$stdout.sync = true&lt;/code&gt; to this example because it circumvents some output buffering that is a whole separate discussion.)&lt;/p&gt;
&lt;p&gt;Here’s a version that’s harder to read but nice because it uses colors:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;ruby&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt; -e&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;    $stdout.sync = true;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;    10.times {|i| (i.even? ? STDOUT : STDERR).puts i}&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;  &amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &amp;gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;sed&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; $&apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;s/^/&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;\e&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;[32m[STDOUT]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;\e&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;[0m /&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;)&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;  2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &amp;gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFCB6B&quot;&gt;sed&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; $&apos;&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;s/^/&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;\e&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;[31m[STDERR]&lt;/span&gt;&lt;span style=&quot;color:#BABED8&quot;&gt;\e&lt;/span&gt;&lt;span style=&quot;color:#C3E88D&quot;&gt;[0m /&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#89DDFF&quot;&gt; &amp;gt;&amp;amp;2)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of program output where odd numbers are prefixed with STDERR in red and even numbers are prefixed with STDOUT in green&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1038&quot; height=&quot;753&quot; src=&quot;https://ryangreenberg.com/_astro/color-coded-stdout-stderr.B_DEiq7n_koWW.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The uses for this technique seem pretty limited because in almost every other case I can imagine you would be better off with a regular pipe.&lt;/p&gt;
</content:encoded></item><item><title>`cmd + option + L` to navigate to Downloads</title><link>https://ryangreenberg.com/til/go-to-downloads/</link><guid isPermaLink="true">https://ryangreenberg.com/til/go-to-downloads/</guid><pubDate>Mon, 22 Jun 2026 17:02:01 GMT</pubDate><content:encoded>&lt;p&gt;For &lt;em&gt;years&lt;/em&gt;–maybe decades–I have repeated these steps when facing an open/save dialog box to select a file.&lt;/p&gt;
&lt;p&gt;If the file I want is on the Desktop, I use &lt;code&gt;cmd + shift + D&lt;/code&gt; to navigate there. If it’s in my Downloads folder, which is common, I use this keyboard Rube Goldberg machine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Press &lt;code&gt;cmd + shift + H&lt;/code&gt; to navigate to my home directory&lt;/li&gt;
&lt;li&gt;Start typing “Dow” to select Downloads&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;cmd + down&lt;/code&gt; to open the selected folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“Ryan,” you might say gently, “There’s a button right there. You know you could just click it?”&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of macOS open/save dialog box with “Downloads” in the sidebar, circled in red&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;850&quot; height=&quot;596&quot; src=&quot;https://ryangreenberg.com/_astro/downloads-in-open-save-dialog.gYh0oDNw_Z1Fi4j9.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The heart wants what it wants (to keep its hands on the keyboard).&lt;/p&gt;
&lt;p&gt;But it turns out that every shortcut listed in the Finder’s Go menu also works in Open/Save dialog boxes.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of macOS Finder’s Go menu showing destinations and their corresponding keyboard shortcuts. Documents: cmd + shift + O; Downloads: cmd + option + L; Desktop: cmd + shift + D; etc.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;418&quot; height=&quot;974&quot; src=&quot;https://ryangreenberg.com/_astro/go-menu.hLSV8mrI_Z2gSzVS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I must have missed &lt;code&gt;cmd + option + L&lt;/code&gt; (presumably for down&lt;strong&gt;L&lt;/strong&gt;oads). Now I use it all the time.&lt;/p&gt;
</content:encoded></item><item><title>`cmd + 9` to select last tab</title><link>https://ryangreenberg.com/til/cmd-9-last-tab/</link><guid isPermaLink="true">https://ryangreenberg.com/til/cmd-9-last-tab/</guid><pubDate>Fri, 19 Jun 2026 17:58:11 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;Animated screenshot of Chrome browser tabs showing the first tab selected after pressing cmd + 1, the second tab selected after pressing cmd + 2, and the last of many tabs selected after pressing cmd + 9.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;219&quot; src=&quot;https://ryangreenberg.com/_astro/tabs.qTvA7YH2_Z2m67u2.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I keep Gmail and my calendar in the first two tabs of my browser and often use &lt;code&gt;cmd + 1&lt;/code&gt; or &lt;code&gt;cmd + 2&lt;/code&gt; to jump to them. The shortcuts continue, with &lt;code&gt;cmd + 3&lt;/code&gt; selecting the third tab, &lt;code&gt;cmd + 4&lt;/code&gt; selecting the fourth, and so on. I rarely jump to the fourth or seventh tab and never the ninth. But &lt;code&gt;cmd + 9&lt;/code&gt; doesn’t jump to the ninth tab, it goes to the last, which I actually want quite often. That’s the stuff I opened most recently!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cmd + option + left&lt;/code&gt; and &lt;code&gt;cmd + option + right&lt;/code&gt; navigate between tabs if you’re close to the one you want.&lt;/p&gt;
</content:encoded></item><item><title>Change the timezone for a command with `TZ`</title><link>https://ryangreenberg.com/til/tz-env-var/</link><guid isPermaLink="true">https://ryangreenberg.com/til/tz-env-var/</guid><pubDate>Wed, 17 Jun 2026 13:40:04 GMT</pubDate><content:encoded>&lt;p&gt;The &lt;code&gt;TZ&lt;/code&gt; environment variable changes the selected timezone for the duration of a command (or at least commands that are aware of &lt;code&gt;TZ&lt;/code&gt;). It’s handy to use with the &lt;code&gt;date&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class=&quot;astro-code material-theme-palenight&quot; style=&quot;background-color:#292D3E;color:#babed8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Tue May  5 09:34:58 EDT 2026&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ TZ=UTC date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Tue May  5 13:34:58 UTC 2026&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I actually added &lt;code&gt;alias utc=&quot;TZ=UTC date&quot;&lt;/code&gt; to my shell so that I can double-check the time when I’m looking at logs with UTC times.&lt;/p&gt;
</content:encoded></item><item><title>WhatsApp has per-conversation member tags</title><link>https://ryangreenberg.com/til/whatsapp-member-tags/</link><guid isPermaLink="true">https://ryangreenberg.com/til/whatsapp-member-tags/</guid><pubDate>Mon, 15 Jun 2026 13:51:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m part of several WhatsApp chats for my kids: one for each class, one for each activity. Each of them is filled with dozens of parents and in many cases I want to know “whose parent are you?” Some parents actually change their name to include their children’s names. This morning in one of the chats I saw one parent’s name and underneath “Daniel’s Mom”. What’s that?!&lt;/p&gt;
&lt;p&gt;It’s a new-ish feature launched in January 2026: member tags. You can set a description that appears alongside your name for each individual conversation. This way in the soccer chat I can mark myself as a coach, and in each classroom chat I can mark myself as the relevant child’s parent.&lt;/p&gt;
&lt;p&gt;Scroll to the members section of the chat details, then tap “Add member tag” to edit.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of the member list of a WhatsApp chat, with one user circled in red. One of the actions on the circled member is “Add member tag”.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1206&quot; height=&quot;698&quot; src=&quot;https://ryangreenberg.com/_astro/chat-member-list.DJ1ajoss_Pu5F7.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of dialog, “Add member tag”. The tag “Samantha’s Dad” is entered and below it shows a preview with the user’s name and tag.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1206&quot; height=&quot;1151&quot; src=&quot;https://ryangreenberg.com/_astro/add-member-tag.DTbn2Ud9_1FYWrl.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;More details on the &lt;a href=&quot;https://faq.whatsapp.com/1342437846952487/?cms_platform=iphone&amp;amp;helpref=platform_switcher&quot;&gt;support page&lt;/a&gt; (which is blank for “Web”; presumably the feature isn’t available there yet)&lt;/p&gt;
</content:encoded></item><item><title>`cmd + tab` has a bunch of extras</title><link>https://ryangreenberg.com/til/cmd-tab-extras/</link><guid isPermaLink="true">https://ryangreenberg.com/til/cmd-tab-extras/</guid><pubDate>Fri, 12 Jun 2026 15:09:47 GMT</pubDate><content:encoded>&lt;p&gt;On macOS &lt;code&gt;cmd + tab&lt;/code&gt; brings up the application switcher. “Yes,” you say, “I have used a computer before.”&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Screenshot of macOS application swicher showing several applications with the Finder icon selected&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;119&quot; src=&quot;https://ryangreenberg.com/_astro/macos-application-switcher-500.ZFgBJ5Ab_ZjPO6y.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;But there are a bunch of extras!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;if you keep the cmd key depressed, you can use &lt;code&gt;tab&lt;/code&gt;, &lt;code&gt;shift + tab&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;, the left/right arrow keys, or even the mouse to select the application to activate.&lt;/li&gt;
&lt;li&gt;press &lt;code&gt;q&lt;/code&gt; when an application is selected to quit the program&lt;/li&gt;
&lt;li&gt;press &lt;code&gt;h&lt;/code&gt; when an application is selected to hide it&lt;/li&gt;
&lt;li&gt;press up/down when an application is selected to show all its windows in an Exposé view&lt;/li&gt;
&lt;li&gt;if you are dragging a file, you can drop it onto an icon in the switcher (I can’t say I’ve ever used this one. I typically drag-and-drop to the Dock but maybe there are cases where the application switcher is more convenient)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>