Using the Powershell to parse columns out of strings
I've been kicking the tyres on Docker, and after a fairly short while I noticed that my list of containers was getting a little full. I decided to clean up, and after a quick look at the documentation, realised that I'd first have to run "docker ps -a" to get a list of all my containers, and then filter the list to get the ones I wanted to delete. (The alternative, was to read through the list, and manually execute "docker rm" on each one that I wanted to delete, and I'm far too lazy for that.)
Here's what the output from "docker ps -a" looks like
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f7a3b9bb073c dominiccronin/gentoo "/bin/bash" 33 minutes ago Exited (127) 33 minutes ago adoring_bell
2ec710c32df0 dominiccronin/gentoo "/bin/bash" 16 hours ago Exited (0) About an hour ago hungry_pare
7805ed925e51 gentoo/portage "sh" 16 hours ago Created portage
43c207846b56 dominiccronin/gentoo "/bin/bash" 16 hours ago Exited (127) 16 hours ago big_goodall
bbcc2e6d87d1 dominiccronin/gentoo "/bin/bash" 18 hours ago Exited (0) 18 hours ago infallible_mayer
f710c351291d ubuntu:14.04 "C:/Program Files/Git" 8 months ago Created hopeful_archimedes
94acf6155aba ubuntu:14.04 "C:/Program Files/Git" 8 months ago Created drunk_mahavira
e5bf3c39aa9e ubuntu:14.04 "C:/Program Files/Git" 8 months ago Created desperate_pasteur
22ace2ca4ba1 ubuntu "C:/Program Files/Git" 8 months ago Created furious_brattain
a20746611b7b 67af10dd2984 "/bin/sh -c '/usr/gam" 9 months ago Exited (0) 9 months ago berserk_goodall
398be811cb6a 67af10dd2984 "/bin/sh -c '/usr/gam" 9 months ago Exited (0) 9 months ago fervent_torvalds
6363467ab659 67af10dd2984 "/bin/sh -c '/usr/gam" 9 months ago Exited (0) 9 months ago grave_bardeen
b21bbf5103f0 67af10dd2984 "/bin/sh -c '/usr/gam" 9 months ago Exited (0) 9 months ago ecstatic_feynman
56f1700ba2ca 67af10dd2984 "/bin/sh -c '/usr/gam" 9 months ago Exited (0) 9 months ago elated_elion
0d41f9675f61 docker/whalesay "cowsay boo-boo" 9 months ago Exited (0) 9 months ago hopeful_brown
7309c5215e9f docker/whalesay "cowsay fooobar" 9 months ago Exited (0) 9 months ago berserk_payne
23c1b894cec2 docker/whalesay "whalesay fooobar" 9 months ago Created lonely_jones6
6a8c27a31740 docker/whalesay "cowsay boo" 9 months ago Exited (0) 9 months ago mad_jones
e5ca9dec78bc docker/whalesay "cowsay boo" 9 months ago Exited (0) 9 months ago sleepy_ardinghelli
43c4d5c7a996 hello-world "/hello" 9 months ago Exited (0) 9 months ago cocky_khorana
cbfe9e33af32 hello-world "/hello" 9 months ago Exited (0) 9 months ago mad_leakey
The "hello, world" examples for Docker are all based on Docker's "theme animal", which is a whale, so if I could identify all the items where the image name contained the string "whale", I'd be on to a good thing. The only problem was that when you run a docker command like this in the powershell, all you get back is a list of strings. The structure of the columns is lost. A quick google showed that there is a Powershell module that might allow me to be even more lazy in the future but the thought of not being able to do it directly from the shell irritated me. So... here goes... this is how you do it:
docker ps -a | %{,@($_ -split ' {2,}')} | ?{$_[1] -match 'whale'} | %{docker rm $_[0]}
Yes, yes, I get it. That looks like the aftermath of an explosion in the top row department of a keyboard factory, so let's take it down a bit.
The interesting part is probably the second element in the pipeline. So after "docker ps -a" has thrown a list of strings into the pipeline, the second element is where I'm deconstructing the string into its constituent columns. The '%' operator is shorthand for 'foreach', so every line will be processed by the script block between the braces, and the line itself is represented by the built-in variable '$_'. (In the third, element you can see a similar construction but with a '?', so instead of a 'foreach', it's a 'where'.)
You can use a Regex with the split operator, and here I've used ' {2,}' to indicate that if there are 2 or more spaces together, I wish to use that as a column separator. Some of the columns are free text, with spaces in them, so I'm taking this pragmatic approach to avoid matching on a single space. Of course, there will be edge cases that break this, so I heartily recommend that you test the results first before actually doing 'docker rm'. Just replace the last element with something like "%{$_[1]}".
Having got the line split into columns, the next challenge is the PowerShell itself. If you throw anything that looks like a collection into the pipeline, it will get automatically unwrapped, and each item will be processed separately in the next block. So here, I'm wrapping the split in an array expression @(), and then preceding that with a comma. The comma operator is used to join a list of items into an array. Usually, this is something like 'a','b','c' - but it works just as well with a single operand, and so ,@(...) gets us an array containing an array. Then when it gets unwrapped by the pipeline, we have just the array containing the split fields. This means that in the third pipeline element we can filter on the value of $_[1] which is the IMAGE field. The fourth element actually invokes "docker rm" using the CONTAINER ID ($_[0]).
I've used Docker as the basis for this example. Just for the record, using the Docker Powershell module I mentioned, I managed to remove all my Ubuntu containers like this:
Get-Container | ?{$_.Image -match 'bun'} | Remove-Container
But as, I said, I'm just using Docker as an example. This PowerShell technique will also help you in many situations where there isn't a module available for the task at hand.