<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title><![CDATA[Jake McCrary's articles on linux]]></title>
  <link href="https://jakemccrary.com/atom.xml" rel="self"/>
  <link href="https://jakemccrary.com/"/>
  <updated>2026-08-17T20:14:29+00:00</updated>
  <id>https://jakemccrary.com/</id>
  <author>
    <name><![CDATA[Jake McCrary]]></name>
  </author>
  <entry>
    <id>https://jakemccrary.com/blog/2024/06/16/scheduling-cron-tasks-in-mixed-time-zones/index.html</id>
    <link href="https://jakemccrary.com/blog/2024/06/16/scheduling-cron-tasks-in-mixed-time-zones/index.html"/>
    <title><![CDATA[Scheduling cron tasks in mixed time zones]]></title>
    <updated>2024-06-16T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><p>Have you ever needed to schedule a repeating task on a Linux host? If so, you&apos;ve probably reached for cron. cron is widely available and reliable; it is a great choice for scheduling tasks.</p><p>Sometimes you find yourself scheduling a task and, ideally, you&apos;d be scheduling that task referencing a different time zone. This is a common need if your programs are interacting with systems hosted in different areas of the world. If one system you interact with starts up at 7 AM <code>Europe/London</code> and another at 8 AM <code>America/New_York</code>, it would be much better to schedule your program to run using times specified in those time zones.</p><p>Why is that preferred?</p><ul><li>If you schedule in your host time zone, you have to convert from the other time zone to your own. This is error prone.</li><li>Different time zones have different Daylights savings shifts. Having to adjust your schedule when your host or target time zone shifts is error prone.</li></ul><p>Luckily, you can do this with cron! At least, with the cronie implementation.</p><p>You do this by specifying the time zone in the crontab with the <code>CRON_TZ</code> variable. Any line after a <code>CRON_TZ</code> specification is scheduled in the specified time zone. This persists until the next <code>CRON_TZ</code> value is specified.</p><p>Below is a sample crontab that schedules four tasks. One is scheduled in the host time zone, two in <code>America/New_York</code>, and one in <code>Europe/London</code>.</p><pre><code>0 7 * * * echo &quot;run at 7 AM in the host time zone&quot;

CRON_TZ=America/New_York
0 7 * * * echo &quot;Run at 7 AM New York&quot;
10 7 * * * echo &quot;Run at 7:10 AM New York&quot;

CRON_TZ=Europe/London
* 8 * * * echo &quot;Run at 8 AM London&quot;
</code></pre><p>The one gotcha with this is that cronie&apos;s behavior is unspecified if the scheduled time ends up in the daylights savings shift of the host machine<a href="#fn-1" id="fnref1"><sup>1</sup></a>. So make sure you don&apos;t do that.</p><p>My team at work has been taking advantage of this feature since early 2023 for scheduling all of our processes start and end times. It has been working great. Prior to figuring<a href="#fn-2" id="fnref2"><sup>2</sup></a> this out, the fall and spring time shifts were sources of issues as various countries shifted on different days. That entire source of problems has been solved through scheduling tasks in the proper time zone.</p><ol class="footnotes"><li class="footnote" id="fn-1"><p>We have unit tests that confirm someone hasn&apos;t configured a task to run within one of these periods.<a href="#fnref1">↩</a></p></li><li class="footnote" id="fn-2"><p>Figuring this out was a bit of a chore. Even the Linux experts I talked to weren&apos;t aware of being able to do this. Digging through the source of cronie was how I figured this out. Hopefully this article makes it easier for the next person. Though, now that I know the <code>CRON_TZ</code> solution, it is pretty easy to search and find other folks talking about this.<a href="#fnref2">↩</a></p></li></ol></div>]]></content>
  </entry>
  <entry>
    <id>https://jakemccrary.com/blog/2020/08/31/utilities-i-like-selecta/index.html</id>
    <link href="https://jakemccrary.com/blog/2020/08/31/utilities-i-like-selecta/index.html"/>
    <title><![CDATA[Utilities I like: selecta]]></title>
    <updated>2020-08-31T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><p><a href="https://github.com/garybernhardt/selecta">Selecta</a> is a command-line utility that gives you the power to fuzzy select items from a list of text. What does that mean? It means you pipe <code>selecta</code> a list of text on stdin, it helps you make a choice from items in that list, and then <code>selecta</code> prints that choice to stdout.</p><p>Here is an example of me using it to help me narrow in on what file I&apos;d like to pass to <code>wc</code>.</p><video autoplay loop muted playsinline>
  <source src="/images/selecta-search.webm" type="video/webm">
  <source src="/images/selecta-search.mp4" type="video/mp4">
</video><p>In this example, I search for markdown files using <code>ripgrep</code> (<code>rg</code>), type part of a filename, hit enter to select the match, and then see the <code>wc</code> stats of that file. This isn&apos;t the greatest example of using <code>selecta</code> but it adequately shows what it does.</p><p>Some number of years ago, I wrote a script called <code>connect-db</code>. This script used <code>selecta</code>, along with <code>grep</code>, <code>sed</code>, and <code>cut</code>, to provide a very pleasant command-line experience for connecting to known databases. My coworkers and I used this script frequently.</p><p>By combining <code>selecta</code> with other stdin/stdout friendly command-line tools you can build really enjoyable, time-saving tools. <a href="https://github.com/garybernhardt/selecta">Selecta</a> is a useful utility to add to your toolkit.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://jakemccrary.com/blog/2020/04/21/using-bash-preexec-for-monitoring-the-runtime-of-your-last-command/index.html</id>
    <link href="https://jakemccrary.com/blog/2020/04/21/using-bash-preexec-for-monitoring-the-runtime-of-your-last-command/index.html"/>
    <title><![CDATA[Using Bash-Preexec for monitoring the runtime of your last command]]></title>
    <updated>2020-04-21T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><p>My article on <a href="/blog/2015/05/03/put-the-last-commands-run-time-in-your-bash-prompt/">putting the runtime of your last command into your bash prompt</a> is one of my most surfaced-by-google articles. Why is this a great to your prompt?  To quote my previous article:</p><blockquote><p>I’m fairly certain the following scenario has happened to every terminal user. You run a command and, while it is running, realize you should have prefixed it with <code>time</code>. You momentarily struggle with the thought of killing the command and rerunning it with <code>time</code>. You decide not to and the command finishes without you knowing how long it took. You debate running it again.</p></blockquote><blockquote><p>For the last year I’ve lived in a world without this problem. Upon completion, a command’s approximate run time is displayed in my prompt. It is awesome.</p></blockquote><p>I&apos;ve been living without the above problem since sometime in 2014 and not having that problem is still awesome.</p><p>I have made some changes since 2014.</p><p>One change was switching to using <a href="https://github.com/rcaloras/bash-preexec">Bash-Preexec</a> instead of directly using <code>trap</code> and <code>$PROMPT_COMMAND</code> for calling functions to start and stop tracking runtime. Bash-Preexec lets you trigger a function (or multiple) right after a command has been read and right before each prompt.</p><p>The usage is pretty straight forward. In the most basic case, you source <code>bash-preexec.sh</code> and then provide functions named <code>preexec</code>, which is invoked right before a command is executed, and/or <code>precmd</code>, which is invoked just before each prompt. <code>bash-preexec.sh</code> can be downloaded from <a href="https://github.com/rcaloras/bash-preexec/">its repo</a>.  The changes required to move to Bash-Preexec pretty <a href="https://github.com/jakemcc/dotfiles/commit/46fc3dc9d4d7d0d73152c77b7383645af42b3d5d">pretty minimal</a>.</p><p>The other change was introducing the script, <a href="https://github.com/jakemcc/dotfiles/blob/9c8c0315f35b55df6cef7e21261e3dcbbfac86e1/home/.bin/format-duration#L3-L4">format-duration</a> by <a href="https://twitter.com/gfredericks_">Gary Fredericks</a>, to humanely format the time. This script converts seconds into a more readable string (example: 310 to <code>5m10s</code>)</p><p>Here is a screenshot of everything in action (with a reduced prompt, my normal one includes git and other info).</p><img src="/images/runtime-humane-example.png" alt="Command line prompt showing runtimes of previous commands" width=320 height=150><p>Below is a simplified snippet from my <code>.bashrc</code> that provides runtimes using both of these additions.</p><pre><code class="language-bash">preexec() {
  if [ &quot;UNSET&quot; == &quot;${timer}&quot; ]; then
    timer=$SECONDS
  else 
    timer=${timer:-$SECONDS}
  fi 
}

precmd() {
  if [ &quot;UNSET&quot; == &quot;${timer}&quot; ]; then
     timer_show=&quot;0s&quot;
  else 
    the_seconds=$((SECONDS - timer))
    # use format-duration to make time more human readable
    timer_show=&quot;$(format-duration seconds $the_seconds)&quot; 
  fi
  timer=&quot;UNSET&quot;
}

# Add $last_show to the prompt.
PS1=&apos;[last: ${timer_show}s][\w]$ &apos;

# a bunch more lines until the end of my .bashrc
# where I include .bash-preexec.sh
[[ -f &quot;$HOME/.bash-preexec.sh&quot; ]] &amp;&amp; source &quot;$HOME/.bash-preexec.sh&quot;
</code></pre><p>No more wondering about the runtime of commands is great. Introducing <code>format-duration</code> made reading the time easier while Bash-Preexec made reading the implementation easier. I highly recommend setting up something similar for your shell.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://jakemccrary.com/blog/2020/02/25/auto-syncing-a-git-repository/index.html</id>
    <link href="https://jakemccrary.com/blog/2020/02/25/auto-syncing-a-git-repository/index.html"/>
    <title><![CDATA[Auto-syncing a git repository]]></title>
    <updated>2020-02-25T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><p>I&apos;m currently keep notes on my computer using plain text and <a href="https://orgmode.org/">Org mode</a>.</p><p>I keep my notes in a git repository in my home directory, <code>~/org/</code>. I want my notes to be synced between my computers without me thinking about it. Historically, I&apos;ve reached for something like Google Drive or Dropbox to do this but this time I reached for git and GitHub.</p><p>Below is the script that I ended up cobbling together from various sources found online. The script pushes and pulls changes from a remote repository and works on my macOS and linux machines.</p><p>The loop starting on line 38 does the work. Whenever a file-watcher notices a change or 10 minutes passes, the loop pulls changes from a remote repository, commits any local changes, and pushes to the remote repository. The lines before this are mostly checking that needed programs exist on the host.</p><p>I keep this running in a background terminal and I check periodically to confirm it is still running. I could do something fancier but this isn&apos;t a critical system and the overhead of checking every couple days is nearly zero. Most of the time checking happens by accident when I accidentally maximize the terminal that runs the script.</p><p>I&apos;ve been using this script for a long time now and I&apos;ve found it quite useful. I hope you do too.</p><pre><code class="language-bash">#!/bin/bash

set -e

TARGETDIR=&quot;$HOME/org/&quot;

stderr () {
    echo &quot;$1&quot; &gt;&amp;2
}

is_command() {
    command -v &quot;$1&quot; &amp;&gt;/dev/null
}

if [ &quot;$(uname)&quot; != &quot;Darwin&quot; ]; then
    INW=&quot;inotifywait&quot;;
    EVENTS=&quot;close_write,move,delete,create&quot;;
    INCOMMAND=&quot;\&quot;$INW\&quot; -qr -e \&quot;$EVENTS\&quot; --exclude \&quot;\.git\&quot; \&quot;$TARGETDIR\&quot;&quot;
else # if Mac, use fswatch
    INW=&quot;fswatch&quot;;
    # default events specified via a mask, see
    # https://emcrisostomo.github.io/fswatch/doc/1.14.0/fswatch.html/Invoking-fswatch.html#Numeric-Event-Flags
    # default of 414 = MovedTo + MovedFrom + Renamed + Removed + Updated + Created
    #                = 256 + 128+ 16 + 8 + 4 + 2
    EVENTS=&quot;--event=414&quot;
    INCOMMAND=&quot;\&quot;$INW\&quot; --recursive \&quot;$EVENTS\&quot; --exclude \&quot;\.git\&quot; --one-event \&quot;$TARGETDIR\&quot;&quot;
fi

for cmd in &quot;git&quot; &quot;$INW&quot; &quot;timeout&quot;; do
    # in OSX: `timeout` =&gt; brew install coreutils
    # in OSX: `fswatch` =&gt; brew install fswatch
    is_command &quot;$cmd&quot; || { stderr &quot;Error: Required command &apos;$cmd&apos; not found&quot;; exit 1; }
done

cd &quot;$TARGETDIR&quot;
echo &quot;$INCOMMAND&quot;

while true; do
    eval &quot;timeout 600 $INCOMMAND&quot; || true
    git pull
    sleep 5
    STATUS=$(git status -s)
    if [ -n &quot;$STATUS&quot; ]; then
        echo &quot;$STATUS&quot;
        echo &quot;commit!&quot;
        git add .
        git commit -m &quot;autocommit&quot;
        git push origin
    fi
done
</code></pre></div>]]></content>
  </entry>
  <entry>
    <id>https://jakemccrary.com/blog/2017/05/29/using-comm-to-verify-matching-content/index.html</id>
    <link href="https://jakemccrary.com/blog/2017/05/29/using-comm-to-verify-matching-content/index.html"/>
    <title><![CDATA[Using comm to verify file content matches]]></title>
    <updated>2017-05-29T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><p>I recently found myself in a situation where I needed to confirm that a process took in a tab separated file, did some processing, and then output a new file containing the original columns with some additional ones. The feature I was adding allowed the process to die and restart while processing the input file and pick up where it left off.</p><p>I needed to confirm the output had data for every line in the input. I reached to the command line tool <code>comm</code>.</p><p>Below is a made up input file.</p><pre><code>UNIQUE_ID	USER
1	38101838
2	19183819
3	19123811
4	10348018
5	19881911
6	29182918
</code></pre><p>And here is some made up output.</p><pre><code>UNIQUE_ID	USER	MESSAGE
1	38101838	A01
2	19183819	A05
3	19123811	A02
4	10348018	A01
5	19881911	A02
6	29182918	A05
</code></pre><p>With files this size, it would be easy enough to check visually. In my testing, I was dealing with files that had thousands of lines. This is too many to check by hand. It is a perfect amount for <code>comm</code>.</p><p><a href="https://en.wikipedia.org/wiki/Comm">comm</a> reads two files as input and then outputs three columns. The first column contains lines found only in the first file, the second column contains lines only found in the second, and the last column contains lines in both. If it is easier for you to think about it as set operations, the first two columns are similar to performing two set differences and the third is similar to set intersection. Below is an example adapted from Wikipedia showing its behavior.</p><pre><code>$ cat foo.txt
apple
banana
eggplant
$ cat bar.txt
apple
banana
banana
zucchini
$ comm foo.txt bar.txt
                  apple
                  banana
          banana
eggplant
          zucchini
</code></pre><p>So how is this useful? Well, you can also tell <code>comm</code> to suppress outputting specific columns.  If we send the common columns from the input and output file to <code>comm</code> and suppress <code>comm</code>&apos;s third column then anything printed to the screen is a problem. Anything printed to the screen was found in one of the files and not the other. We&apos;ll select the common columns using cut and, since comm expects input to be sorted, then sort using <code>sort</code>. Let&apos;s see what happens.</p><pre><code>$ comm -3 &lt;(cut -f 1,2 input.txt | sort) &lt;(cut -f 1,2 output.txt | sort)
$
</code></pre><p>Success! Nothing was printed to the console, so there is nothing unique in either file.</p><p><code>comm</code> is a useful tool to have in your command line toolbox.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://jakemccrary.com/blog/2015/05/03/put-the-last-commands-run-time-in-your-bash-prompt/index.html</id>
    <link href="https://jakemccrary.com/blog/2015/05/03/put-the-last-commands-run-time-in-your-bash-prompt/index.html"/>
    <title><![CDATA[Put the last command's run time in your Bash prompt]]></title>
    <updated>2015-05-03T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><blockquote><p>An updated version of this post can be found <a href="/blog/2020/04/21/using-bash-preexec-for-monitoring-the-runtime-of-your-last-command/">here</a></p></blockquote><p>I&apos;m fairly certain the following scenario has happened to every terminal user. You run a command and, while it is running, realize you should have prefixed it with <a href="http://linux.die.net/man/1/time"><code>time</code></a>. You momentarily struggle with the thought of killing the command and rerunning it with <code>time</code>. You decide not to and the command finishes without you knowing how long it took. You debate running it again.</p><p>For the last year I&apos;ve lived in a world without this problem. Upon completion, a command&apos;s approximate run time is displayed in my prompt. It is awesome.</p><h2 id="overview">Overview</h2><p>Most of the code below is from a post on <a href="http://stackoverflow.com/a/1862762/491871">Stack Overflow</a>. It has been slightly modified to support having multiple commands in your <code>$PROMPT_COMMAND</code> variable. Below is a minimal snippet that could be included in your <code>.bashrc</code>.</p><pre><code class="language-bash">function timer_start {
  timer=${timer:-$SECONDS}
}

function timer_stop {
  timer_show=$(($SECONDS - $timer))
  unset timer
}

trap &apos;timer_start&apos; DEBUG

if [ &quot;$PROMPT_COMMAND&quot; == &quot;&quot; ]; then
  PROMPT_COMMAND=&quot;timer_stop&quot;
else
  PROMPT_COMMAND=&quot;$PROMPT_COMMAND; timer_stop&quot;
fi

PS1=&apos;[last: ${timer_show}s][\w]$ &apos;
</code></pre><p>Modify your <code>.bashrc</code> to include the above and you&apos;ll have a prompt that looks like the image below. It is a minimal prompt but it includes the time spent on the last command. This is great. No more wondering how long a command took.</p><p><img alt="Example of prompt" src="/images/prompt-timings.png" /></p><h2 id="the-details">The details</h2><p><code>timer_start</code> is a function that sets <code>timer</code> to be its current value or, if <code>timer</code> is unset, sets it to the value of <code>$SECONDS</code>. <code>$SECONDS</code> is a special variable that contains the number of seconds since the shell was started. <code>timer_start</code> is invoked after every simple command as a result of <code>trap &apos;timer_start&apos; DEBUG</code>.</p><p><code>timer_stop</code> calculates the difference between <code>$SECONDS</code> and <code>timer</code> and stores it in <code>timer_show</code>. It also unsets <code>timer</code>. Next time <code>timer_start</code> is invoked <code>timer</code> will be set to the current value of <code>$SECONDS</code>. Because <code>timer_stop</code> is part of the <code>$PROMPT_COMMAND</code> it is executed prior to the prompt being printed.</p><p>It is the interaction between <code>timer_start</code> and <code>timer_stop</code> that captures the run time of commands. It is important that <code>timer_stop</code> is the <strong>last</strong> command in the <code>$PROMPT_COMMAND</code>. If there are other commands after it then those will be executed and their execution might cause <code>timer_start</code> to be called. This results in you timing the length of time between the prior and current prompts being printed.</p><h2 id="my-prompt">My prompt</h2><p>My prompt is a bit more complicated. It shows the last exit code, last run time, time of day, directory, and git information. The run time of the last command is one of the more useful parts of my prompt. I highly recommend you add it to yours.</p><p><img alt="My prompt" src="/images/my-prompt.png" /></p><h2 id="errata">Errata</h2><p><em>2015/5/04</em></p><p><a href="https://twitter.com/gfredericks_">Gary Fredericks</a> noticed that the original code sample broke if you didn&apos;t already have something set as your <code>$PROMPT_COMMAND</code>. I&apos;ve updated the original snippet to reflect his <a href="https://twitter.com/gfredericks_/status/595249998838800384">changes</a>.</p></div>]]></content>
  </entry>
</feed>
