pet; tuc; nip
Today, we cover three (well, four, really) tools/services that are each 3-letters in length, do one thing, and do it bonkers well.
TL;DR
(This is an AI-generated summary of today’s Drop.)
- pet: A command-line snippet manager written in Golang, designed to simplify the management and retrieval of command-line snippets. It enhances productivity by allowing users to save, manage, and share command-line snippets across systems via GitHub Gists or GitLab Snippets. The tool adds context to commands and offers control over their lookup and presentation, aiming to provide sanity on the CLI for frequently used commands. More details can be found on its GitHub page.
- tuc: A modern replacement for the
cutCLI command, written in Rust, featuring support for regular expressions and the ability to transform output streams into different formats like JSON. It simplifies extracting specific data from files, such as the leaf nodes from a CSV file, with enhanced functionality overcut. For more information and to try it out, visit tuc’s GitHub page and its online playground. - nip.io: A service that maps any IP address to a hostname, facilitating the use of non-public routable IP addresses in development environments where a hostname is required. It supports various formats, with or without a name, and is particularly useful for apps, frameworks, or servers that require a fully-qualified domain name instead of an IP address. For additional features, including IPv6 support and TLS, the related service sslip.io is recommended.
pet

If you’re even remotely like me (in the following aspect), you likely execute repetitive commands or scripts on a fairly regular basis. This can be prone to error, but taking the time to compartmentalize and document these idioms can be hard to mentally justify. With pet — a pretty simple command-line snippet manager — we can help make this chore a bit less painful, and also get a productivity boost while we’re at it.
This 3-letter Golang CLI tool operates on a fairly straightforward premise: it lets us save, manage, and retrieve command-line snippets without leaving the terminal. This lets us focus more on their core tasks, with the added ability to share these snippets across systems via GitHub Gists or GitLab Snippets.
What makes this tool slightly more useful than, say, any of the leveled-up Bash/Zsh enhanced history looker-uppers, is that you get to add context to the commands, and have some decent control over how the command are looked up and presented.
This will not replace any code snippet managers you have, but it may give you some additional sanity on the CLI, especially for commands you do not use frequently, but do use more than once.
The README does a ton of heavy lifting, so I’ll leave you in their capable hands.
tuc

I have always had a hate-hate relationship with the cut CLI command. My disdain for it and frustration at trying to get it to do something is likely why I became somewhat of an awk expert (back in the day).
What if I told you there was a modern replacement for it that is written in Rust, has some fancy features — including support for regular expressions — and, can even transmogrify the output stream into other formats like JSON?
Oh, I have your interest, now?
Well, tuc is said modern replacement and it is bonkers cool. Let’s say we want all the leaf nodes from the seminal D3 flare CSV example data file. For those that do not know what that “flare” is, it’s just a way to represent a hierarchy of nodes. This is a snippet from the file:
id,value flare, flare.analytics, flare.analytics.cluster, flare.analytics.cluster.AgglomerativeCluster,3938 flare.analytics.cluster.CommunityStructure,3812 flare.analytics.cluster.HierarchicalCluster,6714 flare.analytics.cluster.MergeEdge,743 flare.analytics.graph, flare.analytics.graph.BetweennessCentrality,3534 flare.analytics.graph.LinkDistance,5731 flare.analytics.graph.MaxFlowMinCut,7840 flare.analytics.graph.ShortestPaths,5914 flare.analytics.graph.SpanningTree,3416 flare.analytics.optimization, flare.analytics.optimization.AspectRatioBanker,7074 flare.animate, flare.animate.Easing,17010 flare.animate.FunctionSequence,5842 flare.animate.interpolate, flare.animate.interpolate.ArrayInterpolator,1983 flare.animate.interpolate.ColorInterpolator,2047 flare.animate.interpolate.DateInterpolator,1375 flare.animate.interpolate.Interpolator,8746 flare.animate.interpolate.MatrixInterpolator,2202 flare.animate.interpolate.NumberInterpolator,1382 flare.animate.interpolate.ObjectInterpolator,1629 flare.animate.interpolate.PointInterpolator,1675 flare.animate.interpolate.RectangleInterpolator,2042 flare.animate.ISchedulable,1041 flare.animate.Parallel,5176 flare.animate.Pause,449 flare.animate.Scheduler,5593 flare.animate.Sequence,5534 flare.animate.Transition,9201 flare.animate.Transitioner,19975 flare.animate.TransitionEvent,1116 flare.animate.Tween,6006
With tuc, this is super easy:
$ tuc -e "[\.,]" -f '-2' < flare.csv | tail -n +2 flare analytics cluster AgglomerativeCluster CommunityStructure HierarchicalCluster MergeEdge graph BetweennessCentrality LinkDistance MaxFlowMinCut ShortestPaths SpanningTree optimization AspectRatioBanker animate Easing FunctionSequence interpolate ArrayInterpolator ColorInterpolator DateInterpolator Interpolator MatrixInterpolator NumberInterpolator ObjectInterpolator PointInterpolator RectangleInterpolator ISchedulable Parallel Pause Scheduler Sequence Transition Transitioner TransitionEvent Tween
That -e means “use a regular expression” which gives tuc bionic powers compared to the sad, sad cut. Note, too, that we can use negative offsets, so we do not need to know how many chunks things will be separated into.
Now, lets say we do want the last value included and output everything as JSON:
$ tuc -e "[\.,]" -f '-2,-1' --json < flare.csv | tail -n +2 ["flare",""] ["analytics",""] ["cluster",""] ["AgglomerativeCluster","3938"] ["CommunityStructure","3812"] ["HierarchicalCluster","6714"] ["MergeEdge","743"] ["graph",""] ["BetweennessCentrality","3534"] ["LinkDistance","5731"] ["MaxFlowMinCut","7840"] ["ShortestPaths","5914"] ["SpanningTree","3416"] ["optimization",""] ["AspectRatioBanker","7074"] ["animate",""] ["Easing","17010"] ["FunctionSequence","5842"] ["interpolate",""] ["ArrayInterpolator","1983"] ["ColorInterpolator","2047"] ["DateInterpolator","1375"] ["Interpolator","8746"] ["MatrixInterpolator","2202"] ["NumberInterpolator","1382"] ["ObjectInterpolator","1629"] ["PointInterpolator","1675"] ["RectangleInterpolator","2042"] ["ISchedulable","1041"] ["Parallel","5176"] ["Pause","449"] ["Scheduler","5593"] ["Sequence","5534"] ["Transition","9201"] ["Transitioner","19975"] ["TransitionEvent","1116"] ["Tween","6006"]
It has a few more bells and whistles, but if you’re still not convinced to give it a spot on your SSD, check out their online playground, and I think you’ll be a convert.
nip

OK, so this is technically five letters and a period, but Gimli would say it only counts as three, so that’s good enough for me.
We’ve talked about devd before (it’s a Golang on-the-fly web server++), and it has a companion domain named devd.io which resolves to 127.0.0.1, and exists because many apps/frameworks/servers reequire a fully-qualified domain name vs. an IP address to work.
What if you have non-public routable IP addresses that you want to use in your development environment, but have similar “must be a hostname” requirement?
Enter nip.io. (I’m shamelessly stealing the following copy from the site).
It llows you to do that by mapping any IP Address to a hostname using the following formats:
Without a name:
10.0.0.1.nip.iomaps to10.0.0.1192-168-1-250.nip.iomaps to192.168.1.2500a000803.nip.iomaps to10.0.8.3
With a name:
app.10.8.0.1.nip.iomaps to10.8.0.1app-116-203-255-68.nip.iomaps to116.203.255.68app-c0a801fc.nip.iomaps to192.168.1.252customer1.app.10.0.0.1.nip.iomaps to10.0.0.1customer2-app-127-0-0-1.nip.iomaps to127.0.0.1customer3-app-7f000101.nip.iomaps to127.0.1.1
It can do more, but they deserve your 👀, so give it a tap.
If you also need IPv6 support and TLS, hit up sslip.io. You will not be disappointed.
FIN
Remember, you can follow and interact with the full text of The Daily Drop’s free posts on Mastodon via @dailydrop.hrbrmstr.dev@dailydrop.hrbrmstr.dev ☮️
Leave a Reply