Xonsh and Midnight Commander

Posted by Beetle B. on Sat 21 April 2018

At some point in the last year, I switched to the xonsh shell. It is a bash-like shell written in Python. The nice thing about it is you can write your scripts using Python syntax. Another nice thing is you can use it in Windows.

One of the headaches with xonsh is that when you launch midnight commander, navigate to another directory, and exit, the shell does not place you in the directory you last visited. Every shell out there has a special wrapper script to enable this, but xonsh did not. I decided to write a simple alias for mc:

# cd to directory Midnight Commander ended in.
MC_TMP_DIR = '/tmp/mcdir'
def _mc(args):
    if not os.path.exists(MC_TMP_DIR):
        os.mkdir(MC_TMP_DIR)
    if not os.path.isdir(MC_TMP_DIR):
        print("Sorry, I cannot create the temporary directory %s" % MC_TMP_DIR)
    pid = os.getpid()
    mc_filename = os.path.join(MC_TMP_DIR, "%d" % pid)
    $[/usr/bin/mc -P @(mc_filename) @(args)]
    directory = $(cat @(mc_filename)).strip()
    $[cd @(directory)]
    $[rm @(mc_filename)]

aliases["mc"] = _mc

As you can see above, in xonsh, you can assign any Python function to an alias. Some useful information:

  • The $[] syntax means "Run this command as if you were in the shell."
  • The @() syntax inside these means "Evaluate everything inside the parentheses in Python, and put the result here".
  • The $() syntax is like the $[] syntax except it stores the output as a string.

Contrast this with the version for zsh:

MC_USER=`id | sed 's/[^(]*(//;s/).*//'`
MC_PWD_FILE="${TMPDIR-/tmp}/mc-$MC_USER/mc.pwd.$$"
/usr/bin/mc -P "$MC_PWD_FILE" "$@"

if test -r "$MC_PWD_FILE"; then
        MC_PWD="`cat "$MC_PWD_FILE"`"
        if test -n "$MC_PWD" && test -d "$MC_PWD"; then
                cd "$MC_PWD"
        fi
        unset MC_PWD
fi

rm -f "$MC_PWD_FILE"
unset MC_PWD_FILE
unset MC_USER

Ugh! Although arguably more compact, it is hard to read. I had to Google a fair amount to understand the syntax. I could have used the test command in my xonsh version, but the fact that I had to look up what -r, -d and -n do means it will be as unreadable to the next person.