Ask HN: Small scripts, hacks and automations you're proud of?

332 points by ThePhysicist ↗ HN
I sometimes think back fondly of the small automation scripts I wrote over the years, so I wanted to ask you: Which scripts, hacks & automations did you create that your're proud of? Personally, I e.g. built a bot to automatically answer apartment ads [1], a small script to run Borg backup once per day [2], and a small automation to track the temperature in my aquarium [3].

1: https://gist.github.com/adewes/c9b2a71457c6c6f01f2f

2: https://gist.github.com/adewes/02e8a1f662d100a7ed80627801d0a...

3: https://gist.github.com/adewes/7a4c20a5a7379e19d78ba54521d3d...

334 comments

[ 4.2 ms ] story [ 238 ms ] thread
This is more in the "hack" category, but here is a solution for bookmarking directories[1] that I am quite fond of:

  mkdir ~/.marks/
  export CDPATH=.:~/.marks/
  function mark { ln -sr "$(pwd)" ~/.marks/"$1"; }
Then:

  mark @name   # add bookmark called @name for the current directory
  cd @name     # jump to the bookmarked location
  cd @<tab>    # list all available bookmarks
It can list bookmarks, auto-complete, jump to sub-directories within bookmarks, all without introducing any new commands - just `cd`.

[1]: http://karolis.koncevicius.lt/posts/fast_navigation_in_the_c...

I've just spent the last few minutes playing with the bookmarks snippet. This is both elegant and immediately useful to me. Thanks for sharing!
Glad it is useful. Just a note of advice - prefix your bookmarks with a symbol that is not used to start directory names (was @ in my case).

This is useful for two things - 1) avoiding clashes with existing directories, 2) having a quick command to list all the bookmarks: cd @<tab>

Awesome snippet! I organize most of my work using something similar to the Johnny decimal system[0] and use CDPATH for quicker lookups. But I love how you build a dynamic system with a simple oneliner!

[0] https://johnnydecimal.com/

I just use ranger and mark/bookmark directories like in Vim.
Nice!

I gave up on bookmarking so I now use an alias to quickly find what I am looking for with fzf and fd:

  p () { cd $(fd $1 $HOME -d 4 -i -E Pictures -E Desktop | fzf) }
For people interested in this def look into autojump before doing this
I've been trying this for a few days and now noticed u-boot no longer builds with an output dir ( `make O=subdir whatever` ).

It sure took me ages to figure it was CDPATH, but CDPATH it is. I won't retry without making that "cd @" thing a subfunction (that might involve making its own completion, but that's easy compared to debugging make... I still don't know WHY it broke, just that it's CDPATH that broke it -- it decided to duplicate the subdir arg when invoking a submake so make was now left with a weird unknown target, but it obviously wouldn't say that and just failed weirdly)

Interesting, thanks for sharing. I would be tempted to call this a bug in the Makefile if this is the case. CDPATH is a standard bash environment variable, people use it in various ways: https://github.com/search?q=%22export+CDPATH%22&type=code

If all these causes the build to fail then the maintainers of that build should be informed.

I wrote some scripts to automate the tedious parts of my photo-to-texture pipeline. I learned enough of Blender's API to make a small plug-in to handle some of the rendering tasks. None of these are particularly sophisticated or impressive but they've saved me a lot of time.
(comment deleted)
All of my favorite things are little AutoHotkey shortcuts. I have several that basically just execute a Regex replace on whatever text is currently highlighted. It's ridiculously convenient to just have those functions ready to go any time.

But I started accumulating so many of them, it got to be a problem just remembering all the ones I had and how to trigger them. I had to start finding new ways of keeping them all organized. That led to figuring out two things that I'm still really proud of and use every day: custom right-click menus and Emacs-style keychords.

The custom right-click menu is: any time I right-click while also holding down Ctrl or Alt, it opens a right-click dialogue run by AutoHotkey, not the default one in the given context. That lets me visually select what I want to happen rather than needing an arcane key combo for every thing. All of my frequently-used Regex functions are right-click menus now.

The key chords are: if I trigger a "prep" hotkey first—my most-used one is "Alt+G" for "Go-to"—it sets up a keyboard hook that listens for the next key and then dispatches to a function based on that. So, "Alt+G H" would be really easy to remember in my head as "Go to Hacker news." And it pops open a new tab in my browser. I use that one a lot for opening desktop applications—"Alt+O C" maps in my head to "Open vs Code." Super easy to remember.

I've automated 80% of the most common navigation items at my job this way. My coworkers have seen me do it and stopped me to ask, "Wait, tell me how you did that." It looks and feels like magic.

I'm approaching your threshold of "I started accumulating so many of them, it got to be a problem just remembering all the ones I had and how to trigger them"

Can you share a repo with your "custom right-click menus and Emacs-style keychords" to save me some time?

Love hacker news!

I did something similar but using Autoit

I used the dialog box to create little 'applications' such as listing all the printers in the building (along with description) and allowing the staff to choose the printers they wanted to use (back around 2005 ish)

I had loads of these little scripts for staff to use - and some server side stuff (mainly windows) such as generating daily configs and collecting all the logs and looking for differences

I pretty much automated half my job, allowing me to do more interesting stuff

Another interesting trick is to have a capslock layer (or any other "useless" key you may have, but capslock is much easier to reach obviously) with something like:

    ; ahk v1 code
    CapsLock::
      KeyWait, CapsLock
      If (A_PriorKey="CapsLock")
        SetCapsLockState, % GetKeyState("CapsLock","T") ? "Off" : "On"
    Return
    #If, GetKeyState("CapsLock", "P") 
      u:: ; convert selection to code in markdown by pressing CapsLock+u
        SendRaw, ````
        SendInput {Left}
      Return
      ; ...
    #If

I also find myself with way too many obscure Mouse button {1|2}, Ctrl, Alt, Capslock, F{1|12}, etc. incantations I never remember and end up opening the script to search them. I thought about adding pie menus based on context but never got to it. Would you mind sharing a minimal example of your right click menu? Sounds much simpler and accessible. The keyboard hook à la vscode chords also seems pretty useful, will definitely implement that
In 2004, the website All Music Guide did a horrible redesign. I was so irritated that I created a Firefox extension that did only one thing — it made All Music Guide a bit more useable.

To my knowledge, this was the first "site-specific" browser extension, so I wrote a blog post about it:

https://www.holovaty.com/writing/all-music-guide/

Aaron Boodman saw my post and decided to generalize the idea; he created Greasemonkey, which was a single browser extension that could aggregate site-specific JS customizations. In the years since, this idea of "user scripts" has further developed, and I think some browsers even support the concept natively.

In that case, thank you to you both for dramatically improving my web experience -- I don't know that I could stand browsing without uBlockOrigin and ViolentMonkey

I'm not aware of any browsers that support it natively, although TBH there are so many random Firefox forks that any one of them could have just bundled the extension and called it native

Ah, that reminds me of how I modified sites using https://www.proxomitron.info/ , it's a Windows program that would start a local HTTP proxy and you can configure browsers to use this proxy, and it would block ads. This worked really well before the web became HTTPS-crazy.

The way it would block ads is to analyze the files it was fetching for the browser and modifying found keywords, for a crude example all IMG tags with SRC containing the string ad.* would be replaced by gray images.

I also remember using it to "fix" a redesign of a site.

Hah, well here's a thread to latch on to.

I'm pretty sure AllMusic was my source of track lengths for albums. Back in the middle of high school (so just before that redesign maybe?), I had some java utility that (I'm sure I'm imperfectly recalling) I pasted track information into. It then generated the Wikipedia markup equivalent, and I went around editing album pages on Wikipedia adding track lengths (and total album length).

And that's why I have something over 1000 wikipedia edits made a long time ago.

I remember that AMG redesign. Horrible it was. And dog-slow IIRC. Had no idea about your extension and how it inspired Greasemonkey. Cool!
So certainly not impressive, and morally ambiguous, but when chatGPT first released, I worked with it to create a python script that crawled The New Yorker sitemaps, found articles with embedded audio versions, downloaded the mp3 by reconstructing the file url using the article ID in the page source, and then renamed the file to match the page title and saved it to a well structured Google Drive directory. Now when I walk my dog, exercise, or clean, I'll put on a random article and have a much more enjoyable experience than a podcast would offer. The mp3s were publicly available so I think it's fine for personal use?

I'd never written a line of code in my life, so going through the iterative process of getting it functioning and fixing bugs was pretty invigorating. I could see where the generated code was going awry and describe it in natural language, and chatGPT would turn that into syntax and explain why it worked. Definitely a fun way to learn.

I'm sure it's ugly/inefficient, but here's the script:

https://pastebin.com/raw/DuAVAikD

Pretty impressive to be able to create such a script when you haven't programmed before.

I used to freelance when I learnt to code in college and I wrote many such small automation scripts for quite a few people. Looks like the AI is already taking away some jobs ;)

"going through the iterative process of getting it functioning and fixing bugs was pretty invigorating."

Damn right it is! Writing scrapers is definitely the gateway drug of programming. It's such an exciting feeling to step across the boundaries from "passive user at the mercy of the UI" to "I can do anything if I can conceptualise it (and break it up into steps)".

You summed up what dragged me from "Sure I'll learn to program a notepad or todo app so I can learn, but I won't" to "I can do anything if I think about what I want first, then how to get it!"
+1. Writing scrapers and even API calls were the step for me that went from "I'm making my computer do something" to "I'm making my computer do something that's interacting with the outside world on terms that I programmed it to. Whoa! I can do a lot of different variations of this!"
For me it was writing MIRC scripts that monitored the IRC channels I was in. So it was scrapping the content, but not nearly as elegant as what the OP mentioned.
Completely agree. It's often the first "real program" I give to my students when I teach coding and all of them love the high of doing it.
The comments are even more valuable than the code!
I was trying to do this same thing to download my Dalle2 history all at once. but being behind cloudflare made it a bit hard.
Very cool! It sounds like you could have a career in software development if you wanted one.
You might be one of the first of a new generation of programmers who start with no programming skills, bootstrap with chat gpt and reach a high level of capability. Keep hacking!
In terms of shell aliases and helper functions, I actually go out of my way to not allow myself any.

As a young person, I was on a trip to visit another ISP to learn how a VPN would work between our two companies. Their administrator, a wonderful person with 30 years of experience in the telco-turned-ISP world, had all these incredible aliases on her workstation for managing her fleet. But on the new vendor's system, a fresh install of the same OS left her almost unable to perform.

My jobs have all had the same opportunity for the same experience, and I've tried to leave it alone. I spend maybe 2/3 of my time logged into someone else's server, and I don't doubt myself, even knowing fully well it's a personal preference above all.

I feel much the same way, and for much the same reason. I do, however, have a largish fleet of aliases that I've built up over the years.

The vast majority of them are simple short forms. For example `tf` for `terraform` and `tfp` for `terraform plan`. My brain just naturally thknks of the full command when using the short form and, because of this, I'm not at a loss if I'm logged in somewhere without my aliases.

  drag () {
        dragon --and-exit --on-top "$@"
  }
I write a lot of extremely simple but handy shell functions.

This one lets me drag/and drop things out of a terminal session (kind of) into applications with https://github.com/mwh/dragon and i use it way too often!

I have a neat Hacker News scraping setup that I'm really pleased with.

The problem: I want to know when content from one of my sites is submitted to Hacker News, and keep track of the points and comments over time. I also want to be alerted when it happens.

Solution: https://github.com/simonw/scrape-hacker-news-by-domain/

This repo does a LOT of things.

It's an implementation of my Git scraping pattern - https://simonwillison.net/2020/Oct/9/git-scraping/ - in that it runs a script once an hour to check for more content.

It scrapes https://news.ycombinator.com/from?site=simonwillison.net (scraping the HTML because this particular feature isn't supported by the Hacker News API) using shot-scraper - a tool I built for command-line browser automation: https://shot-scraper.datasette.io/

The scraper works by running this JavaScript against the page and recording the resulting JSON to the Git repository: https://github.com/simonw/scrape-hacker-news-by-domain/blob/...

That solves the "monitor and record any changes" bit.

But... I want alerts when my content shows up.

I solve that using three more tools I built: https://datasette.io/ and https://datasette.io/plugins/datasette-atom and https://datasette.cloud/

This script here runs to push the latest scraped JSON to my SQLite database hosted using my in-development SaaS platform, Datasette Cloud: https://github.com/simonw/scrape-hacker-news-by-domain/blob/...

I defined this SQL view https://simon.datasette.cloud/data/hacker_news_posts_atom which shows the latest data in the format required by the datasette-atom plugin.

Which means I can subscribe to the resulting Atom feed (add .atom to that URL) in NetNewsWire and get alerted when my content shows up on Hacker News!

I wrote a bit more about how this all works here: https://simonwillison.net/2022/Dec/2/datasette-write-api/

I actually just opensourced a personal project/tool I've been using often.

https://github.com/robswc/docker-essential-aliases

When deep diving into containers, I got tired of typing/tabbing out container names and docker formatting lol

Nothing revolutionary but it has saved me a ton of combined time.

This seems really nice! Makes me want to build my own with Gum filters, TBH.
I work at a database heavy company as an infra engineer. From time to time we get requests from devs or customer success to run a query on all customer databases to see or certain assertions hold (e.g. If the latest update fixed a problem or estimate the impact of a change).

To automate this process, we have some ansible playbook to automate most of it: give it a query and you'll get a bunch of files with the results.

While useful, the setup and cleanup can get tidious. So, I whipped up a little shell script that creates a temporary directory and symlink it to a location somewhere in $XDG_STATE_HOME. I created a few subcommands to create the directory structure, add a query, run the query, aggregate results and destroy the state. This keeps things simple for me. Even if I forget to cleanup after myself, a reboot will make sure nothing is left behind. The symlink trick to xdg state home helps me a lot to track my state, while I work on a certain problem. It allows me to easily handle multiple request at the same time, when necessary.

I write a ton of CSS and could not find a command line stylesheet checker. I did find the NuHTML checker from W3C, which also checks the file’s CSS: https://github.com/validator/validator/releases/tag/20.6.30

AFAIK it doesn’t have an option to check a standalone CSS file.

I wrote a script to insert a CSS file into a dummy one-line HTML file, so that I can pass a CSS file to the script, and when errors are generated from the CSS, they are given using the correct line number.

I have written many more complex and perhaps more interesting Scripps, but this is the one I am, by far the most proud of.

* scripts not Scripps. Thanks, Siri.
This little script in my i3 keybindings opens a new terminal with the same working directory as the currently focused terminal, pretty useful for my workflow:

  bindsym ctrl+$alt+t exec xdotool getactivewindow getwindowpid | xargs ps | grep "alacritty" && (xdotool getactivewindow getwindowpid | xargs pgrep -P | xargs pwdx | cut -d":" -f 2 | xargs alacritty --working-directory ) || WINIT_X11_SCALE_FACTOR=1 alacritty
https://github.com/pkos98/Dotfiles/blob/master/config/i3/con...

I probably scratched some parts together from the internet a couple of years ago.

`alacritty --working-directory $(xcwd)` would be a bit shorter :)
I've created a tool for replaying HTTP archives:

https://github.com/Tade0/emergency-poncho

There are many like it, but this one is mine.

Useful when you're a front-end developer and the backend app is not easily deployable locally and the test environment is down.

Also with it you can make a blazing-fast, browsable snapshot of JIRA.

I used to work with a cardiac electrophysiology group. The software used for acquisition and analysis was quite old, written for DOS back in the mid-1990s. At acquisition, files were named like YYMMDDXX.0nn where XX stands for the setup where the acquisition took place, and the nn in the end was the ordinal number of each file. These were encrypted binary files, so we were completely dependent on the original software.

At analysis, files were containing multiple sweeps were averaged to reduce random noise (YYMMDDXX.Ann files), then these files were exported as a CSV-like series of time-voltage paires (YYMMDDXX.Tnn files). Tables containing the calculated variables were also generated, and exported (YYMMDD.Nnn files). The fun part is that these files had to be named MANUALLY. Each and every one of them. I can't stress hard enough how repetitive this got... We generated double-digit number of files every day, and analyzing each file thorougly required around 30-50 keypresses to move around in the menu and to name the files. Lucky for me, no mouse use was required, and keypresses could at least be automated.

I used DOSBox on Debian to do the analysis, and I ended up creating a bash script that could automatically analyze whole folders of these files in a few minutes. To achieve this, I generated xmacro files that would be played back while the DOSBox window was opened. Opening the file was also put in these xmacro files. The generation of the files was wrapped inside a bash script that kept track both of the files in the folder and of the files generated by the analysis. If a file was supposed to be there but some something broke inside DOSBox, it would just stop playing the macro for the next file, so it could be restarted relatively easily.

A few months later, I met the guy who wrote the software for our team, and asked him if he could write us a script to unpack the binaries into CSVs. From there, I could come up with my own completely automated solution for analysis, and everything was much-much faster. I also showed him the macro-monster I created. I'm still not sure if he was amazed or he just thought that I was impatient.

I made a small script to get weekly recipes with certain macronutrient composition from chatgpt, then i linked the ingredient list with my supermarket’s api so every monday I get delivered everything
How have you found the quality of chatgpt’s recipes?

    # Sort list by most common terms
    alias sorn='sort | uniq -c | sort -n'
    # Most common IP addresses:
    $ cat access_log | awk '{ print $2 }' | sorn

    # Last 30 modified files.
    function new() {
        ls -lat "$@" | head -30 ;
    }
    # I just downloaded something, where is it?
    $ new ~/Downloads
Years ago, when I was receiving a very high volume of a predictable and pretty malicious looking spam, I wrote an ugly Python script to report the unwanted mail to the originating network owner. It worked well at the time and I think definitely made the lives of spammers more difficult. The volume of spam I receive has been much lower since then.
I keep thinking about publishing it, but I have a script which takes a Raspberry Pi SD card image, extracts it, modifies it (mostly by automatically editing cmdline.txt and fstab) to be suitable for network booting, and then pushes it to my netboot server.

This way I can quickly boot any Pi in my house (and there are quite a few now) to any arbitrary OS image just by renaming a symlink on the server.