Bash chooses which dotfile to source based on how it gets run. If starting from a login shell, ~/.bash_profile will get sourced, but if there’s not a command in there to source your ~/.bashrc, you may find yourself having to exec bash after starting bash. This can be fixed by adding the following line to your ~/.bash_profile:

[ -f ~/.bashrc ]( -f ~/.bashrc .md) && . ~/.bashrc

I also use ~/.bash_profile for setting numlock while in a tty:

case $(tty) in /dev/tty[0-9]*)
	setleds -D +num * (numlock for X is set in ~/.xinitrc)

Main bashrc

Things started getting a little too expansive for a single file, so I split off relevant sections into their own files. Now all my individual utilities have their own file, making troubleshooting and adding functionality much easier. You can find info for each file and what it does in its own section on this page.

General config

I’m not going in to detail about every line, but I’ll hilight the important parts. The rest should be documented in the script itself.

First off, if we’re not running bash interactively, there’s no use for any of the rest of this, so just skip it.

- If not running interactively, don't do anything
[ $- != *i* ]( $- != *i* .md) && return

Another cool option is actually built in to bash: If you call for a directory without any command before it, just cd into that directory.

- If a directory is given without any command, CD into it.
shopt -s autocd

This is where all the other utilities, aliases, and functions get pulled in. Anything in ~/.bashrc.d/ ending in .bash will get pulled in.

for f in ~/.bashrc.d/*.bash; do source "$f"; done

This also removes the need for the local bashrc sourcing that I had in this file previously. If that functionality is needed, simply make a new script in ~/.bashrc.d/ and don’t track it with vcsh.

Ordering can be done by adding numbers to the beginning of filenames. For example: Currently, nothing that I use requires that kind of functionality.


I originally built my prompt using and, while it’s a nice tool for visually building a prompt, it has several limitations on what you’re able to create with it. But more importantly to me, it generates a rediculously long string, defines and resets color for every single character, uses both a color and bold escape sequence to use light/bright colors, mixes raw escape sequences and subshells running tput, and as a result is utterly unreadable and unmaintainable.

So, I replaced it with my own setup that generates the needed color codes on the fly to improve readability. I intentionally put everything in a function and call it immediately so I may use local vars for the color definitions. I didn’t really want to leave them around just in case.

I’m not completely happy with this solution because it causes each of the tput subshells to execute each time the prompt is printed. I would like to change this to quash the extra output this causes when using bash -x, but I would also like to find a solution that minimizes so many subshells just for a prompt, but avoid hard-coding colors so it can be general enough to support any env in which it may be used.


Here is where I set a few environment variables that are useful in one way or another, such as HISTCONTROL, EDITOR, LS_COLORS, and LIBVIRT_DEFAULT_URI.


Most of these are simple creature comforts and are commented with sufficient explanation.

Pkgfile as command_not_found_handle()

This sets up bash’s command_not_found_handle() functionality so that when a command cannot be found in PATH, it will use pkgfile to check for that command in its databases, which are derived from the same databases that pacman -F uses.

To keep these databases updated, pkgfile ships a systemd timer that runs the update command daily.

Colored man Pages

Some color changes in man are almost essential for readability for me, so I define my own.

This can be replicated for any similar program that uses less as its pager.


shot() used to be much more complicated, but after moving to sway, I found that I couldn’t be arsed to reimplement all of its former functionality. It turned out that the majority of the time, I want to select a region, and put it on stdout to be dealt with how I please. If I find that full-screen or all-screen screenshots are more useful to me in the future, I’ll cross that bridge when I come to it.


Found this little function when I wanted to add functionality to shot(). It takes an integer as an argument, then counts down that number of seconds visually.

I no longer use countdown() from shot() directly, but sometimes will in some sort of pipeline in tandem with it. I found that I want that flexibility out in the open for me to use instead of packed behind a bespoke ui for little benefit.


This tool runs the specified command, capturing stdout and stderr, adds the specified command to the beginning of the output, and sends the result to stdout. The output of this command should be suitable to pipe directly into a paste service such as when seeking support via IRC or other text-based method.


In a similar vein to cmdcopy(), this tool runs the specified command, pipes stdout and stderr directly to fb-client, and sets the specified command as the name of the paste using -n.


I started needing more than one python virtualenv, and I wanted easy access to my own specific file structure. Additionally, I wanted the ability to deactivate the venv like I would exit a child shell. This is the solution that I came up with.

A caveat to this is that the prompt modification that venv usually applies is not available using this method. If a prompt modification is desired, it needs to be taken care of elsewhere. I take care of it in my prompt setup detailed here.


Sometimes you just need to figure out what font provides a specific character. This function provides that solution.

Resolve IP Addresses to MAC Addresses with arping

This utility comes in handy when I just want a MAC address as a string without having to dig through nmap output for it.

Unfortunately not all devices are cooperative to this method, so ymmv.

Get Dell Service Tag

I work with Dell machines a lot, and when dealing with hardware problems, it’s nice to have the service tag handy. Lucky for me, the service tag is easily retrieveable using dmidecode(1), so I made a function for it.

As an added bonus, the -l option will print the url for that product’s support page.

sing() and note2freq()

I was having trouble writing music scripts for the pcspkr because all the examples that I could find only used frequency numbers as arguments to beep, and it was difficult to map frequency numbers to musical note letters in the process of making my own tunes. note2freq() solves this issue by taking a musical note in the form of <letter>[accidental]<octave>, where:

  • <letter> is one of A through G
  • [accidental] is one of:
    • b for flat
    • s for sharp
    • omitted for a natural note
  • <octave> is 1 through 7

sing() takes a list of notes in this same form along with note length in ms, uses note2freq() to translate each one to a frequency number and constructs a string of arguments that beep can use to play the entire tune in one go.

Both of these in tandem make beep tune writing way easier.


This function is both an example of how sing() is used, as well as a tool in its own right.

I wanted a script that played a tune after a long-running script, but play different tunes based on the exit code of the preceding command. This was my cute Portal-themed implementation of that idea.

Proper tune selection depends on triumph() being able to read the exit code from the previous command. For example, for usage in a one-liner script, use something similar to <targetcmd>; triumph. Avoid using || or && in place of the ;.


See: Weechat.

Thurstylark's Wiki

Half brain dump, half documentation practice.

Details about how I configure my shell

Last Modified: 2021-09-25