Ask HN: Can I see your scripts?

374 points by fastily ↗ HN
A few weeks ago, I asked if I could see your cheatsheets (https://news.ycombinator.com/item?id=31928736) and I was really impressed by all the high quality responses!

Today I'm asking about your scripts.

Almost every engineer I know has a collection of scripts/utilities for automating ${something}. I'm willing to bet that HN users have some of the most interesting scripts on the internet.

So that said, could I please see your scripts?

I'll go first: https://github.com/fastily/autobots

306 comments

[ 5.4 ms ] story [ 268 ms ] thread
My scripts are usually for work so they don't make sense outside of that.

One I was particularly proud of/disgusted by was one that allowed me to jump around a network with a single command despite access being gated by regional jumphosts..

You are warned: https://git.drk.sc/-/snippets/107

Another script I wrote for our devs to get access to MySQL in production on GCP; the intent was for the script to be executable only by root and allow sudo access to only this script: that means also ''chmod ugo-rwx gcloud'' too though: https://git.drk.sc/-/snippets/98

I have another script to generate screenshots from grafana dashboards since that functionality was removed from grafana itself (https://github.com/grafana/grafana/issues/18914): https://git.drk.sc/-/snippets/66

Another time I got annoyed that Wayland/Sway would relabel my screens on successive disconnect/reconnects (IE my right screen could be DP-1 or DP-7 or anything in between randomly); so I wrote a docking script which moves the screens to the right place based on serial number: https://git.drk.sc/-/snippets/74

> One I was particularly proud of/disgusted by

I can relate! I think it just reflects the nature of the problem space. The script is gnarly because the thing one is trying to do is gnarly. Utility is the driving force, as far as I'm concerned.

The following aren't as gnarly as yours, but served their purpose nicely in that little project's context. I like to put/accumulate project-related automations in a `./bin` in my projects.

https://gitlab.com/nilenso/cats/-/tree/master/bin

(comment deleted)
(comment deleted)
AutoHotkey script, pressing Caps Lock+Left Mouse Button simulates 50 mouse clicks per second until the left mouse button is pressed:

  ~LButton up::
    if (GetKeyState("CapsLock", "P")) {
      while(!GetKeyState("LButton", "P")){
        MouseClick, left
        sleep 20      
      } 
    }
    return
Useful in situations when you need to click a lot :)

Also a bookmarklet that you use to turn any page dark with a click:

    javascript:document.querySelectorAll('\*').forEach(e=>e.setAttribute('style','background-color:#222;background-image:none;color:#'+(/^A|BU/.test(e.tagName)?'36c;':'eee;')+e.getAttribute('style')))
From here: https://github.com/x08d/222
my autohotkey is super simple and mainly to be vimmy in things like native controls (ALT-K and ALT-J for things like drop downs) which means I almost never need to touch the arrow keys

  <!k::Send {Up}
  <!j::Send {Down}
  Capslock::Esc
What do you use this for?
Messed up user interfaces and games, e.g.: https://www.decisionproblem.com/paperclips/
TIL about Paperclips. Curse you for posting that link! At least I managed to release the hypno drones though...
Ah Universal Paperclips... See you in the future then. Also: Kittens game by bloodrizer (and no, you won't be getting out of this one as easily as you might get out of Universal Paperclips, so search with care if you need to do anything productive for the near future.)
I use AutoHotkey for short-term automation of programs, helped by this entry for "when I save the Autohotkey config in notepad with ctrl+s, reload the script in AutoHotkey so it works immediately":

    ~^s::
        WinGetActiveTitle, currentWinTitle
            If InStr(currentWinTitle, "AutoHotkey.ahk - Notepad")
                Reload
        Return
Then entries which automate typical Windows keyboard actions, like this one below which triggers on alt+3 and moves around the fields on some specific GUI program which had no other automation support for what I was doing:

    !3::Send {alt down}a{alt up}{Tab}{Tab}{Tab}{Tab}{Tab}{Tab}Position{Shift down}{Down}{Shift up}{Del}{Tab}{Tab}
Workflow becomes:

- have something mildly repetitive to do, notice how to do parts of it with keyboard only.

- right click AutoHotkey icon in taskbar, edit (opens Notepad).

- change some of these automations, alt+1, alt+2, alt+3, ...

- press ctrl+s to save and reload.

- switch back to the program and use the hotkey immediately to begin helping.

- repeat switching to AutoHotkey and the program, tweaking and adding more.

It's amenable to the kind of occasional task which has no easy proper automation, or is a one-off and isn't worth more time to do it through proper interfaces. Things like a vendor support telling you to "go through every affected record and toggle X field off and on again".

I have my notes in Dendron which is basically a directory of yaml files. I often need to search through the notes so I made the below

search_notes() { input=$(rg -v '(\-\-)|(^\s*$)' --line-number /home/user/some-dir | fzf --ansi --delimiter : --preview 'batcat --color=always {1} --highlight-line {2}' --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' | choose -f : 0) if [[$input = ""]]; then else less $input fi }

It uses various linux utilities including fzf and batcat(https://github.com/sharkdp/bat) to open a terminal with all the places where my query comes up (supporting fuzzy search). Since the workhorses are fzf and ripgrep its is quite fast even for very large directories.

So i will do `search_notes postgres authentication`. I can select a line and it will open the file in less. Works like a charm!

Related to writing scripts on Mac OS, I highly recommend rumps (https://rumps.readthedocs.io) to show anything you want in your status bar.

For example, I have one script that uses rumps to show how many outdated homebrew packages I have (and also as a convenient shortcut to update those packages in the dropdown menu). I also have a second script that uses it to show a counter for open pull requests that I need to review (with links to individual PRs in the dropdown menu). It's great!

Result looks like this: https://imgur.com/yy6GlYk.jpg

Never heard of rumps, but I have been using xbar (renamed from BitBar) for years and it does the same.

My examples: https://imgur.com/a/SrjG1xe

I basically do all my local management from there, no need to run scripts manually, no need to click around in Finder manually, I even added a command to quickly copy my email, simply because it is so low friction to do it.

Hands down the best tool I've ever used

And there goes the rest of my work week. Wow....
Okay it's 2022 and you still don't want to run nepomunk, a holistic semantic filesystem approach never happened and you're stuck with a million files in your Download folder, home folder etc.

So I use this script to give me a nice work environment, based on each day.

Every time you open bash, it'll drop you into today's directory. (~/work/year/month/day/)

When I think about stuff it's like.. oh yeah I worked on that last week, last year, etc - the folder structure makes this a lot easier, and you can just write 'notes' or 'meeting-with-joe' and you know the ref date.

For your bashrc:

  alias t='source /path/to/today'

  t
Now every day you'll know what you worked on yesterday!

  # this is where you'll get dropped by default.

  calvin@bison:~/work/2022/07/10$

  calvin@bison:~/work/2022/07/10$ ls
  WardsPerlSimulator.pl

  calvin@bison:~/work/2022/07/10$ cd ..; ls;
  01  02  04  05  06  07  08  09  10
  calvin@bison:~/work/2022/07$ cd ..; ls
  01  02  03  04  05  06  07
 calvin@bison:~/work/2022$ cd ..; ls
  2021  2022
additionally you'll get a shortcut, you can type 't' as a bash fn, or go to ~/t/ which is symlinked and updated everytime you run today (which is everytime you open bash or hit 't'. this is useful if you want to have Firefox/Slack/whatever always save something in your 'today' folder.

https://git.ceux.org/today.git/

> Okay it's 2022 and you still don't want to run nepomunk, a holistic semantic filesystem approach never happened

Do you mean Nepomuk, that thing from KDE, which dead like 10+ years ago?

> Every time you open bash, it'll drop you into today's directory. (~/work/year/month/day/)

Ok, that's an interesting Idea, but isn't this more akin to Gnome Zeitgeist? When aimed Nepopmuk at journaling?

> https://git.ceux.org/today.git/

But where is the script? I only see the readme, no code for creating the folder.

> Do you mean Nepomuk, that thing from KDE, which dead like 10+ years ago?

Yeah. I mean I think the pitch when KDE4 launched was like... let's rethink how we deal with our files as less discrete paths and more like easily findable stuff.

> But where is the script? I only see the readme, no code for creating the folder.

whoops, I added it!

I've pair-programmed a lot this year, and some of my colleagues tend to like the "Co-authored-by: ..." message because they like that due attribution is given regardless of who was in control of the keyboard.

I eventually got tired of writing that manually, so I wrote a small

  git co-commit --thor ...
that works just like 'git commit', except it adds another line to the commit message template.

Placing it in e.g. ~/bin/git-co-commit and having ~/bin in your $PATH will enable it as a git sub-command.

I've never had a use for this before, and I don't think I'll need it much beyond this team, but this was my first git sub-command that wasn't trivially solvable by existing command parameters (that I know of).

https://gist.github.com/sshine/d5a2986a6fc377b440bc8aa096037...

wait wait wait... are you saying that having an executable at ~/bin/git-co-commit will automatically create `git co-commit`? Wouldn't it merely create a 'git-co-commit' command you can access as that user as long as it's in your $PATH? What is integrating it in the `git` program?? Or was it just a typo in your example and you actually type `git-co-commit` and not `git co-commit`?
Git will automatically prefix a subcommand with `git-` and try to execute a command with that name.

https://blog.sebastian-daschner.com/entries/custom-git-subco...

Many "built-in" Git commands are themselves separate executables. My Linux machine has them in `/usr/lib/git-core/`, and my macOS machine has them in `/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core/`.

I had the same reaction!... but just tested it and lo and behold it works!

    [user@user ~]# cat > ~/bin/git-test-me <<'EOF'
    #!/bin/bash
    
    echo hi
    EOF
    [user@user ~]# chmod +x ~/bin/git-test-me
    [user@user ~]# git test-me
    hi
Try 'strace git blah', it will be educational.
yep! all git subcommands are separate binaries. Check out `ls /usr/lib/git-core` (that's the path on my machine). Or you can `locate git-add` or something to check where they're located on your machine. I have a tiny fzf script called `git select` to give me a nice interface for selecting branches I've recently worked on. Just call the script `git-select` and stick it somewhere in your path. In the spirit of this question, my one is here:

  !/bin/zsh

  alias git_my_branches="git for-each-ref --sort=committerdate refs/heads/ --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(color:red)%(objectname:short)%(color:reset) - %(contents:subject) - %(authorname) (%(color:green)%(committerdate:relative)%(color:reset))'"

  local selected_branch=$(git_my_branches --color | fzf --ansi --tac | awk '{print $1}')
  if [ "$selected_branch" != '*' ]; then
          git checkout $selected_branch
  fi
Correct. Git will try to run a built in subcommand, and if it doesn't exist, it'll fall back to trying to run `git-some-command`.

Many Linux tools do this. Rust's cargo is another.

In fact, `man` itself can take multiple positional arguments and will concatenate them with a hyphen to perform the lookup.

What does "--thor" mean?
Looking at the linked script, it indicates which cow orker to credit.

To the OP: You might be able to simplify the script by using `git commit --trailer …`. Or maybe you tried that and it didn't display the message in the editor window satisfactorily?

Thanks for the hint, `--trailer` is a nicer solution than overriding the commit template!

For some reason `--trailer` is not available on my system, so I'd need to upgrade git, it seems.

Probably an example of a name, so that the added commit comment line has “Coauthored-by: Thor”
Have you checked out https://github.com/git-duet/git-duet/ ?

You configure a ~/.git-authors file with people with whom you regularly pair, and use `git duet [author-1] [author-2]` to set primary and secondary commit authors. Env variables set whether you want `Signed-off-by` or `Co-authored-by` trailers.

Thanks for the hint!

There seems to be at least these three advantages over my approach:

  - `git duet` has neat syntax for attributing more than two people.
  - `git duet` lets me enter a mode where it keeps attributing my co-authors.
  - `git duet` keeps the authors in a separata data file, not in the script.
I might consider switching for the next small project. :-)
It also can automatically rotate authors if you've got people eager for attribution. When I was just starting out pairing, it felt really good to join a project and immediately get commits on a new repo.
I use a generic version of this.

My .gitconfig has:

  [pretty]
  co-authored-by = Co-authored-by: %an <%ae>
  [alias]
  co-authored-by = log -1 --pretty=co-authored-by --regexp-ignore-case --author
Now `git co-authored-by Tom` generates a Co-authored-by: trailer for the last person named Tom who committed to the repo. Typically I'd just do `:r !git co-authored-by Name` in vim (mapped to \gc to save typing).
To make zsh complete these commands:

    zstyle ':completion:*:*:git:*' user-commands ${${(M)${(k)commands}:#git-*}/git-/}
Or specify just the ones you want to be completed:

    zstyle ':completion:*:*:git:*' user-commands foo:'description for foo'
(from /usr/share/zsh/functions/Completion/Unix/_git)

  autoload -U add-zsh-hook

  add-zsh-hook chpwd source_env

  source_env() {
        if [[ -f .env && -r .env  ]]; then
                source .env
        fi
  }
be careful what you clone and browse around with that
https://github.com/learnbyexample/command_help/blob/master/c...

Command help, inspired by http://explainshell.com/ to extract help text from builtin commands and man pages. Here's an example:

    $ ch ls -AXG
           ls - list directory contents

           -A, --almost-all
                  do not list implied . and ..

           -X     sort alphabetically by entry extension

           -G, --no-group
                  in a long listing, don't print group names

---

https://github.com/learnbyexample/regexp-cut/blob/main/rcut is another - uses awk to provide cut like syntax for field extraction. After writing this, I found commands like `hck`, `tuc` written in Rust that solves most of the things I wanted.

I will steal that too..
Ohhhh that's lovely
Has anybody stumbled upon a similar tool that does this in reverse? For example, I want to find a curl option that has a "redirect" word in its description.
Simple command line utility to display charging (or discharging) rate in watts on linux. You might have to modify battery_directory and the status/current_now/voltage_now names based on laptop brand, but Lenovo, Dell and Samsung seems to use this convention.

    #!/usr/bin/python3

    battery_directory = "/sys/class/power_supply/BAT1/"

    with open(battery_directory + "status", "r") as f:
        state = f.read().strip()

    with open(battery_directory + "current_now", "r") as f:
        current = int(f.read().strip())

    with open(battery_directory + "voltage_now", "r") as f:
        voltage = int(f.read().strip())

    wattage = (voltage / 10**6) * (current / 10**6)
    wattage_formatted = f"{'-' if state == 'Discharging' else ''}{wattage:.2f}W"

    if state in ["Charging", "Discharging", "Not charging"]:
        print(f"{state}: {wattage_formatted}")

Output:

Charging: 32.15W

Discharging: -5.15W

My ASUS Zephyrus G15 has a BAT0 with power_now (in microwatts) instead of current_now and voltage_now.

I have in my shell history which I occasionally use:

  while true; do echo -n '^[[34m'; date --iso-8601=seconds | tr -d '\n'; echo -n '^[[m: battery contains ^[[31m'; echo "scale=3;$(cat /sys/class/power_supply/BAT0/energy_now)/1000000" | bc | tr -d '\n'; echo -n 'Wh^[[m, '; [ "$(cat /sys/class/power_supply/BAT0/status)" = "Discharging" ] && echo -n 'consuming' || echo -n 'charging at'; echo -n ' ^[[32m'; echo "scale=3;$(cat /sys/class/power_supply/BAT0/power_now)/1000000" | bc | tr -d '\n'; echo 'W^[[m'; sleep 60; done
(With ^[ being actual escape, entered via Ctrl-V Escape, because I find writing the escape codes literally easier and more consistent than using echo -e or whatever else.)

It’ll show lines like this every minute (with nice colouring):

  2022-08-16T01:07:31+10:00: battery contains 76.968Wh, charging at 0W
My PinePhone has an axp20x-battery with current_now and voltage_now, like your various laptops except that while discharging it gets a negative current_now, which makes perfect sense to me but which doesn’t seem to match your laptops (since you add the negative sign manually in your script) or my laptop’s power_now (which is likewise still positive while discharging).
Bank reconcilliation in Xero has no "auto match if reference and date are the same" option, so I (very crudely) scripted one. It'll run until it encounters a mismatch (which for the company I work for is basically never). Paste into console.

function go() {

    let line = document.querySelector('#statementLines .line');

    if (line) {

        let leftAndRight = line.querySelectorAll('.statement');

        let left = leftAndRight[0];
        let right = leftAndRight[1];

        if (right.hasClassName('matched')) {

            let leftDetails = left.querySelectorAll('.info .details-container .details span');
            let leftDate = leftDetails[0].textContent;
            let leftReference = leftDetails[1].textContent;

            let rightDetails = right.querySelectorAll('.info .details-container .details span');
            let rightDate = rightDetails[0].textContent;
            let rightReference = rightDetails[2].textContent;

            rightReference = rightReference.replace('Ref: ', '');

            if (Date.parse(leftDate).getTime() == Date.parse(rightDate).getTime() && leftReference.toLowerCase() == rightReference.toLowerCase()) {


                var okButton = line.querySelector(".ok .okayButton");

                console.log(leftReference);

                okButton.click();

                var waiter = function () {

                    if (line.parentNode == null) {

                        go();

                    }
                    else {
                        setTimeout(waiter, 50);
                    }


                };

                setTimeout(waiter, 50);



            }
            else {
                console.log("Details dont match");
            }


        }
        else {
            console.log("Line not matched");
        }

    }
    else {
        console.log("No line found");
    }
}

setTimeout(go, 100);

I switched from Xero to plaintext accounting,[0] and it was a huge step up.

There are several different plaintext accounting tools, but they all support automation like this. I personally use Beancount because I work best in Python.

The other huge advantage is that the "state" of your finances isn't opaque like in Xero. If you realize you've been categorizing certain transactions incorrectly in Xero, it's a hassle to navigate Xero's interface to correct everything, whereas in plaintext accounting it's usually a 2-second find/replace.

The downside is that there's a steep learning curve and the documentation is kind of overwhelming, but once you learn it, it's extremely valuable.

[0] https://plaintextaccounting.org/

Just logged in to tell you that I am working on an alternative to Xero and you just validated one of our UI designs.
My dotfiles: https://github.com/BurntSushi/dotfiles

Here are some selected scripts folks might find interesting.

Here's my backup script that I use to encrypt my data at rest before shipping it off to s3. Runs every night and is idempotent. I use s3 lifecycle rules to keep data around for 6 months after it's deleted. That way, if my script goofs, I can recover: https://github.com/BurntSushi/dotfiles/blob/2f58eedf3b7f7dae...

I have so many machines running Archlinux that I wrote my own little helper for installing Arch that configures the machine in the way I expect: https://github.com/BurntSushi/dotfiles/blob/2f58eedf3b7f7dae...

A tiny little script to recover the git commit message you spent 10 minutes writing, but "lost" because something caused the actual commit to fail (like a gpg error): https://github.com/BurntSushi/dotfiles/blob/2f58eedf3b7f7dae...

A script that produces a GitHub permalink from just a file path and some optional file numbers. Pass --clip to put it on your clipboard: https://github.com/BurntSushi/dotfiles/blob/2f58eedf3b7f7dae... --- I use it with this vimscript function to quickly generate permalinks from my editor: https://github.com/BurntSushi/dotfiles/blob/2f58eedf3b7f7dae...

A wrapper around 'gh' (previously: 'hub') that lets you run 'hub-rollup pr-number' and it will automatically rebase that PR into your current branch. This is useful for creating one big "rollup" branch of a bunch of PRs. It is idempotent. https://github.com/BurntSushi/dotfiles/blob/2f58eedf3b7f7dae...

Scale a video without having to memorize ffmpeg's crazy CLI syntax: https://github.com/BurntSushi/dotfiles/blob/2f58eedf3b7f7dae...

Under X11, copy something to your clipboard using the best tool available: https://github.com/BurntSushi/dotfiles/blob/2f58eedf3b7f7dae...

Here's a shell script for moving files in git between repos, preserving the history, and following that history through the file being renamed:

     git-move src/afile.c src/bfile.c src/cfile.c ../destination/repo
https://gist.github.com/mnemnion/87b51dc8f15af3242204472391f...
simpler than i expected. what was your original use case?
Same as the ongoing use case: breaking submodules of a project out into its own namespace, while preserving the edit history.
JS snippet to sort and return Play Store app reviews by helpfulness:

  var nodes = [...document.querySelectorAll('\*[aria-label="Number of times this review was rated helpful"]')];
  nodes.sort((a, b) => (parseInt(b.innerText) || 0) - (parseInt(a.innerText) || 0));
  nodes.map(e => ([
    parseInt(e.innerText) || 0,
    e.parentNode.parentNode.parentNode.parentNode.parentNode.children[1].textContent.toString().trimStart(),
  ]));
Does a PostgreSQL plpgsql function count as a script?

https://gist.github.com/stuporglue/83714cdfa0e4b4401cb6

It's one of my favorites because it's pretty simple, and I wrote it when a lot of things were finally coming together for me (including GIS concepts, plpgsql programming, and a project I was working on at the time).

This is code which takes either two foci points and a distance, or two foci, a distance and the number of pointers per quadrant and generates a polygon representing an ellipse. Nothing fancy, but it made me happy when I finally got it working.

The use case was to calculate a naive estimate of how far someone could have ridden on a bike share bike. I had the locations they checked out the bike, and where they returned it, and the time they were gone. By assuming some average speed, I could make an ellipse where everywhere within the ellipse could have been reached during the bike rental.

I often have a need to serve a local directory via HTTP. In the old days the built-in Python webserver was enough, but at some point browsers became more aggressive about concurrent connections and the single-threaded `python -m SimpleHTTPServer` would just get stuck if it received two requests at once.

As a workaround, I wrote a small wrapper script that would enable multi-threading for SimpleHTTPServer.

~/bin/http-cwd , Python 2 version (original):

  #!/usr/bin/python
  import argparse
  import BaseHTTPServer
  import SimpleHTTPServer
  import SocketServer
  import sys

  class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
      pass

  def main(argv):
      parser = argparse.ArgumentParser()
      parser.add_argument(
          "--port", type = int, nargs = "?",
          action = "store", default = 8000,
          help = "Specify alternate port [default: 8000]",
      )
      parser.add_argument(
          "--iface", type = str, nargs = "?",
          action = "store", default = "127.0.0.1",
          help = "Specify iface [default: 127.0.0.1]",
      )
      args = parser.parse_args(argv[1:])
      server_address = (args.iface, args.port)
      srv = ThreadedHTTPServer(server_address, SimpleHTTPServer.SimpleHTTPRequestHandler)
      sa = srv.socket.getsockname()
      print "Serving http://%s:%r ..." % (sa[0], sa[1])
      srv.serve_forever()

  if __name__ == "__main__":
      sys.exit(main(sys.argv))
Python 3 version (necessary for platforms that have dropped Python 2, such as macOS):

  #!/usr/bin/python3
  import argparse
  import http.server
  import socketserver
  import sys

  class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
      pass

  def main(argv):
      parser = argparse.ArgumentParser()
      parser.add_argument(
          "--port", type = int, nargs = "?",
          action = "store", default = 8000,
          help = "Specify alternate port [default: 8000]",
      )
      parser.add_argument(
          "--iface", type = str, nargs = "?",
          action = "store", default = "127.0.0.1",
          help = "Specify iface [default: 127.0.0.1]",
      )
      args = parser.parse_args(argv[1:])
      server_address = (args.iface, args.port)
      srv = ThreadedHTTPServer(server_address, http.server.SimpleHTTPRequestHandler)
      sa = srv.socket.getsockname()
      print("Serving http://%s:%r ..." % (sa[0], sa[1]))
      srv.serve_forever()

  if __name__ == "__main__":
      sys.exit(main(sys.argv))
How does this differ from the stdlib approach?

    python -m http.server 8080 --bind 127.0.0.1 --directory your_directory
May I direct your attention to the second sentence of my post?

It fixes an issue in the Python built-in HTTP server that causes it to hang under concurrent connections.

First, run the built-in Python web server:

  [term1]$ python -m SimpleHTTPServer 8080
Then connect to it with a client that doesn't immediately send a request, such as `netcat`. This simulates the behavior of modern browsers, which seem to set up a pool of pre-established connections.

  [term2]$ nc localhost 8080
Now try to get a page from the server via Curl (or wget, etc). It will hang after sending the request, because the server's single thread is trying to serve the idle connection.

  [term3]$ curl -v http://127.0.0.1:8080
  *   Trying 127.0.0.1:8080...
  * TCP_NODELAY set
  * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
  > GET / HTTP/1.1
  > Host: 127.0.0.1:8080
  > User-Agent: curl/7.68.0
  > Accept: */*
  > 
In real life, the behavior I saw was that I'd try to connect to the server with Chrome and it would hang after the pages had partially loaded.
I think this is a problem that's long been fixed; I tested your command and it seems to work as expected for me in Python 3.10 anyway. And I've been using the "python -mhttp.server" frequently for years, and never experienced any of these problems.

  alias makepw='cat /dev/urandom | LC_ALL=C tr -cd A-Za-z0-9,_- | head -c 25; echo'
Any proper password manager will of course be able to supplant tricks like these.
Basically a one-liner version of pwgen.
Makes operating AWS CLI against a user with MFA enabled easier

---------

#!/bin/sh

echo "Store and retrieve session token AWS STS \n\n"

# Get source profile read -p "Source Profile [<profile_name>]: " source_profile source_profile=${source_profile:-'<profile_name>'} echo $source_profile

# Get destination profile read -p "Destination Profile [<profile_name>-mfa]: " destination_profile destination_profile=${destination_profile:-'<profile_name>-mfa'} echo $destination_profile

mfa_serial_number='arn:aws:iam::<id>:mfa/<name>'

echo "\nOTP: " read -p "One Time Password (OTP): " otp

echo "\nOTP:" $otp echo "\n"

output=$(aws sts get-session-token --profile <profile_name> --serial-number $mfa_serial_number --output json --token-code $otp)

echo $output

access_key_id=$(echo $output | jq .Credentials.AccessKeyId | tr -d '"') secret_access_key=$(echo $output | jq .Credentials.SecretAccessKey | tr -d '"') session_token=$(echo $output | jq .Credentials.SessionToken | tr -d '"')

aws configure set aws_access_key_id $access_key_id --profile=$destination_profile aws configure set aws_secret_access_key $secret_access_key --profile=$destination_profile aws configure set aws_session_token $session_token --profile=$destination_profile

echo "Configured AWS for profile" $destination_profile

maybe look into AWS Vault?
I use my system setup script: https://github.com/pcho/binfiles/blob/master/bt. It helps me a lot with setting up new VPS when I need or with daily tasks. While using macOS, I also had this as helpers: https://github.com/pcho/binfiles/blob/master/.archive/setup-..., to set up homebrew. And, https://github.com/pcho/binfiles/blob/master/.archive/setup-..., for a bunch of options as many of build from source works fine in both systems. In .archive folder there’s a lot of other scripts that I used, but tried to incorporate them in bootstrap script.

It also uses my https://github.com/pcho/dotfiles, https://github.com/pcho/vimfiles and https://github.com/pcho/zshfiles