cut & tr

As part of this bash script I worked on recently I have been playing a bit with cut and tr. These are old Unix commands and I haven’t used them that much. It’s mostly been a bit of sed or awk (am no expert in either of them) but as part of creating this script I was re-introduced to cut and tr when Googling for some solution.

The cut command cuts text along a delimiter and can output the fields you choose. The tr command can translate text.

This is not tutorial but here’s an example of where I used these two today. I have a bunch of docker volumes I want to list just the names of. The default output is thus:

Sure, I can pipe a grep kea-knot to get just the lines with volume names, but how can I extract just the volume name? This is where cut comes into play. The following should in theory get me the second column:

This does not however. It only returns a bunch of blank lines. Why? Because I am telling it to cut along a space, and between local and the kea-knot_keaconfig text for instance there’s multiple spaces. This the second column is actually the fifth or sixth column.

So what I need here is a way to trim the spaces (or get cut to match on one or more spaces – it doesn’t seem to do that). Here’s where its buddy tr comes into play. tr can also squeeze characters. So if I do tr -s ' ' it will squeeze multiple spaces into a single one. And you can guess where the story goes from here… I pass that to cut who will dutifully get me the second field as expected. :)

Tada!

In this specific case however if I had read the docker volume command docs a bit more I would have discovered that adding a --quiet switch to docker volume ls would have got me just the volume names!

Oh well. Wouldn’t have had a bit of fun with cut and tr then would I? :o)

Anyways, why was I doing this? Because I wanted to move multiple volumes from one Docker host to another. I had blogged about a single volume previously, now I loop over that:

Copy these over to the destination host and loop over to import them:

Updates: I will add to this blog post as I find more use cases & examples.

Random Numbers

I want to generate a 20 digit random number (to use with an Azure Storage account). /dev/random will give random characters. So pipe it to tr and tell it to ignore anything that’s not a number.

Start with this:

I am not a 100% switched on about LC_CTYPE. Without it tr gives an tr: Illegal byte sequence error. Setting it tells tr what locale to use to interpret the output of /dev/urandom:

LC_CTYPE This variable determines the locale category for character handling functions, such as tolower(), toupper() and isalpha(). This environment variable determines the interpretation of sequences of bytes of text data as characters (for example, single- as opposed to multi-byte characters), the classification of characters (for example, alpha, digit, graph) and the behaviour of character classes. Additional semantics of this variable, if any, are implementation-dependent.

So tr takes the output of /dev/random. The -d switch tells it to delete anything not matching the class I specify [:digit:] (which stands for all numbers). However the -c switch says complement the class I have specified – thus -c [:digit:] says anything which is not a number – and so -dc [:digit:] says delete anything which is not a number.

The end result is I end up with a bunch of random number!

I don’t want so many random numbers, just 20. So I pipe that to head to get just that many:

If I wanted a mix of alphabets and numbers instead I can use the following:

Or for just alphabets:

To give credit where due the above is inspired from this gist. Do check that for more examples.