Bash Completion For Pandoc Is Built In

This is more in the way of a note to myself. I was just starting to write a bash completion script for Pandoc when I came upon this  in the Pandoc documentation:

--bash-completion

Generate a bash completion script. To enable bash completion with pandoc, add this to your .bashrc:

 eval "$(pandoc --bash-completion)"

 

So no need for me to write one. Neat trick, generate your own bash completion script. John McFarlane really is a god. Oh, and the completion is top quality, it knows when you’ve typed an option that takes an input or output format and completes on those and other little tricks. I may end up using some of his tricks for my completions.

A Little Shell Will Fix It

Last night I went to Lights For The Wild at Taronga Zoo. As usual, I took a lot of photos with my DSLR camera, over 200, though a lot of that number are quite similar as I often take two or three to increase the chances of getting the right shot, sometimes I vary the speed so that one is better exposed.

The camera saves both a RAW file​ and a JPEG so I end up with over 400 images. Looking through them in QuickLook in the Finder can be painful as the RAW images take quite a while to load, then you get the problem that when you have decided which of the three shots you want to keep you also have to delete the matching JPEG or RAW file.

The easiest solution to both of these is to only go through the JPEG files and then delete the matching NEF file (which is what the Mac calls the RAW file).

So I open the folder and sort by ‘Kind’ which puts the JPEGs at the top. I then open the first in QuickLook by hitting space and using the up and down arrow keys to move through the list commanddelete deletes a file and displays the next. Easy.

Now I have 80 JPEG files from the original 240. How to get rid of the NEF files that match the JPEG files I have deleted? A little bash programming to the rescue.


for i in *.NEF ; do
  if [ ! -e `basename $i NEF`JPG ]; then 
    rm $i; 
  fi 
done

The secret to this is the basename utility. It’s a neat little tool. Pass it a full file path such as /Users/tonyw/Documents/UselessRamblings.txt and it will return just
the file name without the path, UselessRamblings.txt. It has a matching tool, dirname which returns just the path portion. As you can see from my code basename has another trick, it will happily strip the suffix from the filename if you tell it what to strip.

Now We Have bash Completion For Munki

I’m on a roll. I’ve written the bash completions for Munki.

(tl;dr The completions are on Github )

It’s getting easier to write them. There was one little trick I used that I didn’t
mention in my last post that I thought I’d share. How to use find and replace with
regular expressions to generate some of your code.

For this I use Find... in BBEdit. I started with a list of the commands, one on each
line.

The first thing we need to do is generate a string with each command separated by
a space. This one is trivial. We just find \n and replace it with ` `. The second one is the
hardest. We want each of the switches in the case statement like this:

	repo-add) _autopkg_repo-add ;; 
	repo-delete) _autopkg_repo-delete ;; 
	repo-list) _autopkg_list_processors ;; 
	repo-update) _autopkg_repo-update ;;

when we start with:

repo-add
repo-delete
repo-list
repo-update

The “Find” is the easy part. We want to match everything on a line up to, but not
including, the newline at the end. This looks like (.*)\n – the parentheses define the
part we want to match. Now for the replace – we want a tab, then the name, then a
parenthesis and so on. You can see we need to insert the name into a template twice.
This ends up as \t\1) _autopkg_\1 ;;\n – the \1 means “the first match in the Find”.

So I just enter those into the dialog and hit “Replace All” and the list of commands is
changed into the required bash code. After pasting the result into my script I can
hit “Undo” and the list is back ready for me to use it again. I can even generate
boilerplate code using a different replace:

_autopkg_\1() {
  local cur="${COMP_WORDS[COMP_CWORD]}"
  case "$cur" in
    --*)
      __autopkgcomp "--help ****"
      return
      ;;
  esac
}

The advantage of doing things this way is not just less typing. By generating the code
I can be sure that the switches and the function names are correct and match
each other. (BTW – notice that I have a **** in the boilerplate. This marks where I
need to alter the function and also marks it as not finished.)

Many years ago I was tutored by two of Brain Kernighan’s books – ‘Software Tools’ and
‘The Unix Programming Environment’ and this is exactly the sort of thing he evangelised.
If you can use a tool to write your code, all the better.

bash completion for autopkg

Over the weekend I was feeling a little bored so I decided to try my hand at writing a shell script to add custom completion for autopkg to bash.

(tl;dr – the script is on GitHub.)

I found an example for the zsh shell which lacked a couple of features and I spent some time examining the script for brew so I wasn’t totally in the dark.

There are a number of tutorials available for writing them but none are particularly detailed so that wasn’t much help.

Writing Shell Scripts

The first thing I should say is that I find writing shell scripts totally different to writing for any other language. I probably write shell scripts incredibly old school, shell and C were the two languages I was paid to write way back in the 1980’s. It feels like coming home.

Continue reading

Bash Notebooks in Jupyter

So IPython now has a back end called Jupyter that supports a large number of kernels as well as Python.

This means we can write Jupyter notebooks with bash for example.

Here’s how.

Installing Python 3

The bash kernel requires Python 3 and I’ve only installed Anaconda using Python 2.7. How do we easily get Python 3.4 or later as the kernel requires?

Fortunately conda can create a Python environment with Python 3 in one easy step.

conda create -n py3k python=3 ipython notebook

That creates the environment using Python 3 and adds the IPython and Juypter system with notebook support.

This will give us all we need so then source activate py3k will turn it on.

You can tell you are in a conda environment as the bash prompt is prepended with the name of the environment like so py3k)Air:~/dev/Notebooks tonyw$ Continue reading

Naming Windows and Tabs in Terminal

window_named

Would you like to name your Terminal window?

printf "e]2;`hostname -s`a"

That will turn it into your hostname (minus the domain). I’ve added the above to my standard .bashrc file on all the computers I visit via ssh.

If you want to do it yourself on the fly then

wname()
{
    printf "e]2;$*a"
}

added to your .bashrc file will allow you to say “wname The New Name” at the bash prompt any time you like. If you use tabs rather than multiple windows then:

tname()
{
    printf "e]1;$*a"
}

I’ll leave the function, which I define as ‘hname’, to set it back to the hostname as an exercise for the reader. Oh, and a final tip; you may notice that for the tab the string starts “e]1;” and for the window you replace the ‘1’ with a ‘2’. If you use ‘0’ then you change both the tab and the window name.

If you want to get really slick with your Terminal window name try setting it to the current directory. I now do this in my promptcommand like this:

PROMPT_COMMAND='history -a; history -n; printf "e]1;${PWD}a"'

Add that to your .profile. Now you have a directory path you can even command-click or click and drag on.

Setting up the command line

In a previous post I went through what I install on a fresh Mac. This time I want to go through how I set up bash and a few command line tools for use in Terminal.

I have to admit that I’m a command line nerd. I grew up professionally as a C programmer under Unix System V and back then the command line was all you had. I also do a lot of work on OS X Servers where you don’t want to install too many applications and the shell can be a godsend.

Continue reading