bash: Last Day, The People Who Lived As Many Days as You

Definitions

  • bash: This is the command line, where you can run relatively simple scripted programs, available on all three major computing platforms.
  • SPARQL: On the Internet, there are repositories of information. Some of these repositories are in a format called RDF, or Resource Description Framework. Users of these repositories typically need a subset of the information contained in them. In order to get the desired information, they need a way to query these repositories in a structured way to get the information they want. SPARQL is that querying language.
  • Wikidata is an RDF repository. It is hosted by the same organization as Wikipedia, but it is subject to different rules. I do not think Notability and some of the cultural problems of Wikipedia extend to Wikidata. I’d be happy to hear if anyone is aware of problems in the dataset, since this is one of the few times I’ve worked with it.

Inspiration

Most mornings, my wife and I read The New York Times The Morning Briefing. Typically, this will include an obituary of a celebrity. If the person is less than 80 years old, my wife will say something like, “They died young.” She thinks everyone should live to be a hundred years of age.

I tend to think more relativistically. Someone died young, if they were younger than me. It got me thinking, “Is it possible to write a script to find out who lived exactly the same number of days I have lived today?”

It turns out to be fairly easy to do using bash, a SPARQL query link and Wikidata.

bash script

#!/bin/bash
# variables
BIRTHDAY=$(date -d '2000-01-01' +%s) # enter birthday in YYYY-MM-DD format
TODAYS_DATE=$(date +%s)
DAYS_ALIVE=$(((TODAYS_DATE - BIRTHDAY) / 86400)) # converts seconds to days

# Test output
# echo "birthday: ${BIRTHDAY} | today's date: ${TODAYS_DATE} | days_alive: ${DAYS_ALIVE}"

#url for sparql query of wikidata can be obtained: https://query.wikidata.org/, click link to it below
firefox 'https://query.wikidata.org/embed.html#SELECT%20DISTINCT%20%3Fperson%20%3FpersonLabel%20%3FpersonDescription%20WHERE%20%7B%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22%5BAUTO_LANGUAGE%5D%22.%20%7D%0A%20%20%7B%0A%20%20%20%20SELECT%20DISTINCT%20%3Fperson%20%3FpersonLabel%20%3FpersonDescription%20%7B%0A%20%20%20%20%20%20%3Fperson%20wdt%3AP31%20wd%3AQ5%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20wdt%3AP569%20%3Fborn%3B%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20wdt%3AP570%20%3Fdied%3B%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20wdt%3AP27%20wd%3AQ30%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20BIND(%3Fdied%20-%20%3Fborn%20AS%20%3FageInDays).%20%0A%20%20%20%20%20%20FILTER(%3FageInDays%20%3D%20'"$DAYS_ALIVE"').%20%20%0A%20%20%20%20%7D%0A%20%20%20%20LIMIT%2025%0A%20%20%7D%0A%7D%0A'

bash script output

SPARQL query

You can input the following into the Wikidata SPARQL query interface and change the perimeters. Specifically, the bash variable $DAYS_ALIVE needs to be changed to an integer to work in the query interface, e.g., FILTER(?ageInDays = 11000). You can also do ranges using multiplication, e.g., FILTER(?ageInDays < (31*365) && ?ageInDays > (30*365)), if you want people between the ages of 30 to 31.

SELECT DISTINCT ?person ?personLabel ?personDescription WHERE {
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
  {
    SELECT DISTINCT ?person ?personLabel ?personDescription {
      ?person wdt:P31 wd:Q5;             # any person
              wdt:P569 ?born;            # that has a birth date
              wdt:P570 ?died;            # and a death date
              wdt:P27 wd:Q30             # that was a citizen of the United States
      BIND(?died - ?born AS ?ageInDays). # calculate days they lived
      FILTER(?ageInDays = $DAYS_ALIVE).  # match the number of days to your current number of days alive
    }
    LIMIT 25
  }
}

bash: TOTP From the Terminal With oathtool

TOTP is Time-based One Time Password. Most people use applications on their phone for TOTP, such as andOTP, Google Authenticator, and related apps. But, as we move from using a phone as a second factor for what we are doing on a computer to a phone being the primary way we interact with the Internet, it makes sense to make the computer the second factor. This is the idea behind this script. It is based on analyth’s script, except I stripped out the I/O.

#!/bin/bash

# Assign variables
google=$(oathtool --base32 --totp "YOUR SECRET KEY" -d 6)
wordpress=$(oathtool --base32 --totp "YOUR SECRET KEY" -d 6)
amazon=$(oathtool --base32 --totp "YOUR SECRET KEY" -d 6)

# Print variables
echo "google: ${google} | wordpress: ${wordpress} | amazon: ${amazon}"

This will print:

google: 123456 | wordpress: 123456 | amazon: 123456

However, I didn’t like the idea of my one time password codes only being protected by normal file protections on a Linux system. I thought it should be encrypted with gpg. So, I saved it to a file in my scripts directory, totp, and encrypted it with my public key. If you don’t have a gpg key pair, instructions are available online.

$ gpg -r your@email.com -e ~/pathto/totp

Then, to run the shell script, do:

$ gpg -d ~/pathto/totp.gpg 2>/dev/null | bash

This will prompt you for your gpg password and then run this script. You likely won’t want to remember this string of commands, so you could make your life easier by adding it as an alias under .bash_aliases

alias totp='gpg -d ~/pathto/totp.gpg 2>/dev/null | bash'

Mutt: Viewing Attachments / HTML via .mailcap and a Custom Fortune as a Signature in Mutt

It’s funny how small, trivial things can lead you to make radical changes in the tools you use. As regular readers of this blog know, I collect sayings that I publish every month. I then compile these sayings into a custom fortune file that displays one saying every time I login to my computer or open a new terminal window/shell process.

I recently learned that I can call this custom fortune file as a signature and have one added automatically to every email I write by adding this line to my .muttrc configuration file.

set signature="fortune /usr/share/games/fortunes/cafebedouin -s|"

This is simply calling the fortune program, specifying the location of the file and the -s flag is telling fortune to find a small quote to add. This is a completely trivial feature, but I love it. It is what provided the motivation to actually get mutt to work as my main email client.

My main problem with mutt has been that I couldn’t figure out how to get it to render HTML emails in a readable format, which makes mutt a poor choice as an everyday email client. Half of the emails I receive are in HTML format. The problem, it turns out, is that my email provider encrypts all my email, so I needed an additional line in .mailcap that processes the pgp/mime format, like so:

text/plain; cat %s; copiousoutput
text/html; mkdir -p /tmp/mutt \; cp %s /tmp/mutt \; firefox /tmp/mutt/$(basename %s) &
text/html; lynx -nonumbers -dump %s; copiousoutput; nametemplate=%s.html 
pgp/mime; lynx -dump %s; copiousoutput; nametemplate=%s.html

This points to something I didn’t understand. .mailcap is basically how you tell mutt to process email attachments, and you simply associate file types with programs on your system. There’s also default behavior, where the text/html with copiousoutput will be used when you hit enter by default and when you go to view the attachment, mutt will call the first relevant line in mailcap, as mentioned here.. The same idea applies to other file types, such as images.

image/*; mkdir -p /tmp/mutt \; cp %s /tmp/mutt \; xdg-open /tmp/mutt/$(basename %s) &

So, once the change above is made, you then need to change this line in .muttrc:

alternative_order text/html text/plain text/enriched text multipart/alternative 
auto_view text/html

# Removes temporary attachment files
folder-hook . `rm -f /tmp/mutt/*`

And now, it works beautifully. I’ve completely stopped using thunderbird, and I only use mutt. And, it has improved my email experience so much. I receive something like 50-100 emails a day, most of them newsletters or promotional material from organizations I signed up to hear more about. But, mutt makes it so easy to navigate and delete email.

Since making the transition, my inbox – which I had always relatively good control over and rarely had more than a day’s worth of email in – is down to a couple of leftover emails per day. I read what I want and delete them. I highly recommend making the transition.

bash: Cryptocurrency Prices From the Linux Terminal

#!/bin/bash
printf -v coin '%s' -1   # crypto.sh bitcoin

price() {
  # A function that pulls cryptocurrency price data from coingecko
    
  curl -X 'GET' 'https://api.coingecko.com/api/v3/simple/price?ids='"$1"'&amp;vs_currencies=usd' \
     -H 'accept: application/json' 2> /dev/null | # sends download data to /dev/null
      
  sed  's/.*usd"://' |   # Removes everything before the price
  sed 's/..$//' |        # Removes back two }}
  sed 's/^/\$/'          # Adds dollar sign to the front, returns
}

bitcoin=$(price bitcoin)
ethereum=$(price ethereum)

# Checks to see if there is a command line variable and prints to console
if [[ -z $1 ]]; then
    echo "bitcoin: ${bitcoin} | ethereum: ${ethereum}"
else
    price=$(price $1) # calls function with command line variable
    echo "${1}: ${price} | bitcoin: ${bitcoin} | ethereum: ${ethereum}"
fi

h/t Techstructive for the basic idea. I simplified their code by cutting out the I/O and putting the coin as a variable when calling the script, e.g. crypto.sh bitcoin, and formatting it by piping it through sed. Have I mentioned how much I love sed?

Edit: Modified this on August 12, 2021 so it is now a function and prints a portfolio of coins. I track two or three, and it was getting annoying to have to do them each individually. All you need to do to modify it for the coins you are interested in is create a new function call:

cardano=$(price cardano)

Then add that to both the if and else print results.

    echo "${1}: ${price} | bitcoin: ${bitcoin} | ethereum: ${ethereum} | cardano: ${cardano}"

bash: Number of Days Between Today and Some Future Date

#!/bin/bash                                                        
                                                                   
printf -v date '%(%Y-%m-%d)T\n' -1                                 
echo $(( ($(date -d $1 +%s) - $(date -d $date +%s)) / 86400 )) days

Above is a bash script to output the number of days between today and some future date. Copy it into a file, e.g., diffdate.sh, into a directory, e.g., ~/bin/scripts. Then, enter the directory you saved it to and type to make it executable:

$ chmod +x diffdate.sh

Then, check your .profile to make sure something like this in it:

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then                             
  PATH="$HOME/bin:$PATH"
fi                                                                   

Then, run the script.

$ diffdate.sh 2021-06-01
70 days

I have to figure out the difference between today and some future date all the time for forecasting, and today was the day I finally bothered to figure out how to do it from the command line. I have to start thinking of ways to make shell scripts to do this little tasks that I go to the web for.