This is a lightweight tip that’s definitely just so that I have someplace to look it up, when I need to do it next.

Among the conveniences that I have created on my computer, my favorite is probably opening up the draft for the upcoming Entropy Arbitrage newsletter when I log in. I keep the drafts in a non-obvious folder with many other projects that don’t yet deserve their own repository, so rather than try to remember where the file is when I have something to add for the month, it’s always open in the text editor that I use for most simple work.

Gears

So, this is a walk-through of how that process fits together.

Note that I run Ubuntu at this time. I am well aware that the “Linux community” often has things to say about Ubuntu and Canonical that are somewhat less than kind, but the same community gives a pass to other companies that have the same issues. I also don’t particularly care; it works.

Where to Begin…?

There are many systems that can be considered a Linux startup. Among others that I’ve probably forgotten, there are…

  • System startup, generally used for services, and are generally handled by adding fiddly scripts that need precise names, so that they are started with the right “cohort.” For Ubuntu, systemd handles this, despite a lot of complaints—and unfortunate harassment—from individuals who resent the fact that Linux is no longer primarily for hobbyists using 1990s technology.
  • Logging in requires starting a shell, when the system runs the .profile script. It’s obviously a closer solution.
  • When opening a shell—for a command prompt—other than login, there’s a different file that runs, usually named after the shell. Since I run BASH in most cases, the magic file is .bashrc.
  • Graphical desktop environments have a startup manager. In theory, this should be the “correct” solution, but has limitations on flexibility and isn’t easily made portable.

The systemd option occurs before anybody can log in, so it’s probably not going to work. Using .bashrc doesn’t make much sense, since I don’t always open a shell on startup. That leaves the other two choices.

Proof of Concept

Because it’s more flexible—I can run whatever code I please with whatever conditions I choose—I decided to work with .profile and see how that goes. I added the following to the end of that file.

nohup /usr/bin/gedit ~/path/to/newsletter/$(date +"%Y-%m").md &

If you’re not familiar with what’s happening, here…

  • nohup is an old standard command that means “no hang up,” meaning that the command shouldn’t stop when the shell ends.
  • date takes a formatter, in this case a four-digit year, a hyphen, and a two-digit month.
  • The ampersand (&) says to run the program in the background.

Since gEdit is the default text editor, that works. But it could work better.

Maybe That’s Not the Editor

For a few months, gEdit on Ubuntu had some trouble with resource management. It’d open a large file or many files, stall—which derailed whatever I was doing—and then ultimately crashed. While the relevant developers were fixing that, I moved to a different default editor.

If this ever happens again, I don’t want to need to remember to update my .profile to reflect that change, so we should probably generalize the editor. Unfortunately, that’s a multi-step process.

tempfile=$(mktemp --suffix=.txt)
echo "Time to wake up!" > "$tempfile"
editor=$(mimetype -b "$tempfile" | xargs xdg-mime query default | xargs whereis -b | cut -f2 -d' ')
nohup "$editor" ~/path/to/newsletter/$(date +"%Y-%m").md &
  rm "$tempfile"

For the first two lines, we create a temporary text file with some pointless text, enough for any programs to recognize that it’s just a test file. Then, inside the parentheses—which tells the shell to run the command and save the output to assign to a variable—we get the MIME type of that text file, which should be “text/plain.” The xargs command takes every word in its input and calls a command on it, in this case querying the MIME database for the default handler—if we needed to do this normally, it would look like this.

xdg-mime query default "text/plain"

That gives us the name of the default application. We then use xargs again—we shouldn’t need to, but not doing so threw an error—to ask for the location of “binary files” (non-text) installed with that application’s name. Finally, we pick the first item in that list, after the heading. That gives us a path-and-executable to run, which we store in the $editor variable.

Finally, we use the $editor variable to open the file as above, and delete the temporary file.

That’s much better, except for the…

Dueling Banjos Editors

For some reason, Visual Studio Code and VSCodium open login-shells when they start, which executes the code in .profile, and so brings my mailing list draft to the front whenever I go to edit code. That’s survivable, but not ideal.

However, then—as has probably been mentioned in a developer journal post—I started using RVM to deal with diverging Ruby versions. For some reason that I hope is beyond just being difficult, most critical RVM features only work in login-shells. So now, every shell I open is a login-shell, so that messes around with the editor a lot more than I’d like it to. It probably wouldn’t take long to figure out why—one obvious guess comes to mind while I wrote this, in fact—but if something like this happened twice, it’s going to happen again.

So, the next improvement is to say that we’re effectively only going to do this once per log-in. Or maybe “approximately once” is better. A slip of the finger can accidentally close the editor on startup, so sometimes I’ll want to call it back up. That produces something like this.

uptime=$(cut -f1 -d' ' /proc/uptime | cut -f1 -d'.')
if [ $uptime -lt 60 ]
then
  tempfile=$(mktemp --suffix=.txt)
  echo "Time to wake up!" > "$tempfile"
  editor=$(mimetype -b "$tempfile" | xargs xdg-mime query default | xargs whereis -b | cut -f2 -d' ')
  nohup "$editor" ~/path/to/newsletter/$(date +"%Y-%m").md &
  rm "$tempfile"
fi

This gets the number of seconds that the system has been running and, if it’s less than sixty (one minute), then open the editor. Skip it, if it’s later. So now, if I accidentally close the editor when it opens, I can open a terminal window—which now starts a login-shell—and get it back.

Do We Have the Time? Do We Need It?

An alternate possibility would have been to see if the editor is running, and only open it if not. However, to keep the editor implicit, this would either need to create a new temporary text file for every shell that opens, or need a permanent text file, and hope that nobody ever deletes it, removes it, or replaces the contents with something that mimetype recognizes as different.

Since neither of those possibilities appeals to me, I went with the current up-time, and will deal with re-opening the file at the rare times that I accidentally close the editor after the first minute.

Other Possibilities

The above code is the final version for me, but there’s probably some room for improvement.

For example, dealing with temporary files is always a mild security risk, since another process can snoop on them and modify the contents. I’m willing to risk it, here, since I’m the only person using my computer, the file contains no important information, and its entire lifespan is probably less than a microsecond in any case where I don’t need to immediately reboot the computer.

In addition, since this is for the newsletter, it might make sense to open the current month’s draft and the draft for the previous month, if we haven’t reached the Saturday that it’ll be released.

You get the idea. The reason I chose to use .profile was to get the flexibility in details and conditions, so this can now do anything, effectively. But for most purposes, I’d recommend just using the graphical tool and save the headaches.


Credits: The header image is work, technology, vintage, wheel, retro, clock, by an uncredited PxHere photographer, is made available under the terms of the CC0 1.0 Universal Public Domain Dedication.