The cancellation of Russian music

Free speech in Russia has never been particularly favoured. The Romanov dynasty remained in power long past their expiration date by suppressing waves of free thought, from the ideals of the Enlightenment, to the anti-capitalist ideals of Marx and Engels. At least, until the 1917 Revolution. And even then, the Bolsheviks continue to suppress dissent for the entire seventy-something year history of the Soviet Union. Perestroika and the collapse of the Soviet Union promised change. But the change was fleeting.

Now, Russia is waging a brutal unprovoked war in Ukraine; and Vladimir Putin has used this crisis - one entirely of his own doing - to suppress any vestiges of freedom of expression in Russia. Famously, a young woman was detained in Moscow for displaying a small card on which she had written два слова (literally “two words”.)

This has completely changed my opinions about free speech. Sure, I cheered after the January 6 attempted coup at the United States Capitol when Donald Trump was finally deplatformed. After years of his ignorance, clownishness, and gross incompetence, I was happy to have a break. But the problem doesn’t lie solely in Trump’s malevolent stupidity, although that’s certainly in abundance. The problem is shared with an anti-Enlightenment public that can’t distinguish fact from fiction, that can’t step outside of their own muddling of facts and values, and can’t abide a world in which they don’t always “win.” But suppressing dissent or even nonsense isn’t the solution. A recent editorial in the New York Times would have us do away with the so-called “cancel culture”, but in their formulation of the problem, opinions should be free from shaming and shunning. Coddling ideas and giving them a blanket of insulation against criticism is just another form of censorship. As painful as it can be, I’d rather open the floodgates and allow discourse to flow freely.1

Relatedly, I’ve also changed my mind about punishing artists for political transgressions. In recent weeks, both soprano Anna Netrebko and conductor Valery Gergiev of the Metropolitan Opera and Munich Philharmonic respectively have been dismissed because they failed to unequivocally and publicly disavow support for Putin’s invasion of Ukraine. Anna Netrebko refuses to condemn the war? Well, she has her reasons. Maybe she believes the what the Russian media is spewing. Maybe she doesn’t, but she wants to keep her options open. Who knows? But punishing her amounts to a spiteful act that does nothing to change the course of the war.

Let art stand on its own. In advocating for art being a protected space, I’m not endorsing the New York Times’ opinion that ideas should be protected from criticism. If you put something out into the world, you take the good and the bad. As they say: “If you can’t take the heat, get out of the kitchen.”


  1. But I also advocate for non-algorithmic availability and presentation of content. Discourse can only thrive openly, if power-holders and their algorithms aren’t actively curating content. ↩︎

Bash variable scope and pipelines

I alluded to this nuance involving variable scope in my post on automating pdf processing, but I wanted to expand on it a bit.

Consider this little snippet:

i=0
printf "foo:bar:baz:quux" | grep -o '[^:]\+' | while read -r line ; do
   printf "Inner scope: %d - %s\n" $i $line
   ((i++))
   [ $i -eq 3 ] && break;
done
printf "====\nOuter scope\ni = %d\n" $i;

If you run this script - not in interactive mode in the shell - but as a script, what will i be in the outer scope? And why?

Unless you look carefully, you would think that i should be 3. After all, the while loop exits on a test for equality of i and 3, right? But no, i remains 0 in the outer scope; and this is because each command in a pipeline runs in its own subshell. From the GNU bash manual section on pipelines:

Each command in a pipeline is executed in its own subshell, which is a separate process. (Emphasis is mine.)

The last command in the pipeline in the above example is the while loop, so it’s merrily executing in its own subshell modifying its own i while the outer scope is unaware of what’s happening in this shell. This explains why i remains 0 in the outer scope.

But what are we to do if we want the inner scope to modify i? The key is to set the lastpipe option with shopt -s lastpipe. This option introduced in bash 4.2 forces the last command in the pipeline to run in the outer shell environment. So now if we modify the script with this option:

shopt -s lastpipe

i=0
printf "foo:bar:baz:quux" | grep -o '[^:]\+' | while read -r line ; do
   printf "Inner scope: %d - %s\n" $i $line
   ((i++))
   [ $i -eq 3 ] && break;
done
printf "====\nOuter scope\ni = %d\n" $i;

what is i in the outer scope? Right, it’s 3 this time because the while loop is executing in the shell environment, not in its own subshell.

Automating the handling of bank and financial statements

In my perpetual effort to get out of work, I’ve developed a suite of automation tools to help file statements that I download from banks, credit cards and others. While my setup described here is tuned to my specific needs, any of the ideas should be adaptable for your particular circumstances. For the purposes of this post, I’m going to assume you already have Hazel. None of what follows will be of much use to you without it. I’ll also emphasize that this is a macOS-specific post. Bear in mind, too, that companies have the nasty habit of tweaking their statement formats. That fact alone makes any approach like this fragile; so be aware that maintaining these rules is just part of the game. With that out of the way, let’s dive in.

Bulk rename tags in DEVONthink 3

In DEVONthink, I tag a lot. It’s an integral part of my strategy for finding things in my paperless environment. As I wrote about previously hierarchical tags are a big part of my organizational system in DEVONthink. For many years, I tagged subject matter with tags that emmanate from a single tag named topic_, but it was really an unnecessary top-level complication. So, the first item on my to-do list was to get rid of the all tags with a topic_ first level.

Stripping Russian syllabic stress marks in Python

I have written previously about stripping syllabic stress marks from Russian text using a Perl-based regex tool. But I needed a means of doing in solely in Python, so this just extends that idea.

#!/usr/bin/env python3

def strip_stress_marks(text: str) -> str:
   b = text.encode('utf-8')
   # correct error where latin accented ó is used
   b = b.replace(b'\xc3\xb3', b'\xd0\xbe')
   # correct error where latin accented á is used
   b = b.replace(b'\xc3\xa1', b'\xd0\xb0')
   # correct error where latin accented é is used
   b = b.replace(b'\xc3\xa0', b'\xd0\xb5')
   # correct error where latin accented ý is used
   b = b.replace(b'\xc3\xbd', b'\xd1\x83')
   # remove combining diacritical mark
   b = b.replace(b'\xcc\x81',b'').decode()
   return b

text = "Том столкну́л Мэри с трампли́на для прыжко́в в во́ду."

print(strip_stress_marks(text))
# prints "Том столкнул Мэри с трамплина для прыжков в воду."

The approach is similar to the Perl-based tool we constructed before, but this time we are working working on the bytes object after encoding as utf-8. Since the bytes object has a replace method, we can use that to do all of the work. The first 4 replacements all deal with edge cases where accented Latin characters are use to show the placement of syllabic stress instead of the Cyrillic character plus the combining diacritical mark. In these cases, we just need to substitute the proper Cyrillic character. Then we just strip out the “combining acute accent” U+301\xcc\x81 in UTF-8. After these replacements, we just decode the bytes object back to a str.

Accessing Anki collection models from Python

For one-off projects that target Anki collections, I often use Python in a standalone application rather than an Anki add-on. Since I’m not going to distribute these little creations that are specific to my own needs, there’s no reason to create an add-on. These are just a few notes - nothing comprehensive - on the process.

One thing to be aware of is that there must be a perfect match between the Anki major and minor version numbers for the Python anki module to work. If you are running Anki 2.1.48 on your desktop application but have the Python module built for 2.1.49, it will not work. This is a huge irritation and there’s no backwards compatibility; the versions must match precisely.

Converting Cyrillic UTF-8 text encoded as Latin-1

This may be obvious to some, but visually-recognizing character encoding at a glance is not always obvious.

For example, pronunciation files downloaded form Forvo have the following appearance:

pronunciation_ru_оÑ‚бывание.mp3

How can we extact the actual word from this gibberish? Optimally, the filename should reflect that actual word uttered in the pronunciation file, after all.

Step 1 - Extracting the interesting bits

The gibberish begins after the pronunciation_ru_ and ends before the file extension. Any regex tool can tease that out.

accentchar: a command-line utility to apply Russian stress marks

I’ve written a lot about applying and removing syllabic stress marks in Russian text because I use it a lot when making Anki cards.

This iteration is a command line tool for applying the stress mark at a particular character index. The advantage of these little shell tools is that they can be composable, integrating into different tools as the need arises.

#!/usr/local/bin/zsh

while getopts i:w: flag
do
    case "${flag}" in
        i) index=${OPTARG};;
        w) word=${OPTARG};;
    esac
done

if [ $word ]; then
    temp=$word
else
    read temp
fi

outword=""
for (( i=0; i<${#temp}; i++ )); do
    thischar="${temp:$i:1}"
    if [ $i -eq $index ]; then
        thischar=$(echo $thischar | perl -C -pe 's/(.)/\1\x{301}/g;')
    fi
    outword="$outword$thischar"
done

echo $outword

We can use it in a couple different ways. For example, we can provide all of the arguments in a declarative way:

sterilize-ng: a command-line URL sterilizer

Introducing sterilize-ng [GitHub link] - a URL sterilizer made to work flexibily on the command line.

Background

The surveillance capitalist economy is built on the relentless tracking of users. Imagine going about town running errands but everywhere you go, someone is quietly following you. When you pop into the grocery, they examine your receipt. They look into the bags to see what you bought. Then they hop in the car with you and keep careful records of where you go, how fast you drive, whom you talk with on the phone. This is surveillance capitalism - the relentless “digital exhaust” left by our actions online.

Using Perl in Keyboard Maestro macros

One of the things that I love about Keyboard Maestro is the ability to chain together disparate technologies to achieve some automation goal on macOS.

In most of my previous posts about Keyboard Maestro macros, I’ve used Python or shell scripts, but I decided to draw on some decades-old experience with Perl to do a little text processing for a specific need.

Background

I want this text from Wiktionary:

to look like this: