08 Oct 2016

Bash Tricks

A page to collect together various bash tricks I’ve needed.

Recursive Search and Replace

Edit files in place, applying a simple search and replace:

find [1,2]* -type f -exec sed -i.bak 's|foo|baz|' {} +

Note the + at the end, which allows find to pass multiple files to the command to be exec-ed. This reduces the number of invocations of sed.

Insert a line (if it’s not there already)

The scenario: I have some YAML meta data on a blog post, and I want to insert a permalink key after the author key, but only if the file doesn’t already have a permalink entry.

Solution:

find . -type f ! -exec grep -q 'permalink:' {} \; -exec gawk -i inplace '/author:/ { print; print "permalink: /blog/:title"; next}1' {} \;

This requires GNU Awk for the in-place editing of a file.

Batch Rename

Adding a prefix:

for f in *.png; do mv "$f" "prefix-${f}"; done

Removing a prefix:

or f in *.png; do mv "$f" "${f#prefix}"; done

Thank you:

Stomp on the Content of a Set of Files

For example: you want to find ever build.properties file and set the content to be sbt.version=1.0.1:

find . -name build.properties -exec bash -c 'echo "sbt.version=1.0.1" > {}' \;

Note the need to escape the echo command because otherwise the > part is executed early by Bash. Thanks to SO 8828974.

Remove Recent Docker Container

This is handy if, for example, you’re running a database in Docker, and you want to totally blow it away.

Solution:

Stop the docker container, and then go check for the ID:

docker ps -a | head -2

That will give you the CONTAINER ID along with other useful sanity-checking information. E.g.,

CONTAINER ID        IMAGE                              COMMAND                  CREATED             STATUS                      PORTS               NAMES
9b30fe9e0da6        postgres:latest                    "docker-entrypoint..."

Next, remove the container:

docker rm 9b30fe9e0da6 

…or whatever the container id should be.

Remove Multiple Docker Containers

You try to remove a Docker image but discover it’s referenced by containers. You want to remove those containers.

Solution:

docker ps -a | grep /book | awk '{print $1}' | xargs  docker rm

Here we’re grep-ing for /book which happens to be an image name I’m interested in.

Email on failure

Use exit 0 for success, and anything else for failure. Then…

some_command > /home/richard/logs/some_command.log 2>&1
if [ $? -ne 0 ]; then
  mail user@example.com -s "Some command failed" < /home/richard/logs/some_command.log
fi

Cleaning up from a script

See: http://redsymbol.net/articles/bash-exit-traps/

For example (from that post):

#!/bin/bash
scratch=$(mktemp -d -t tmp.XXXXXXXXXX)
function finish {
  rm -rf "$scratch"
}
trap finish EXIT

Scatter plots

Here I’m using rg (faster grep) and cut to pick out x and y values I want to plot:

$ rg ':E:' atari.log | cut -d' ' -f 5,6 | head
9 235.00000
18 235.00000
27 300.00000
...

Then using GNU Plot to plot to the terminal and to a PNG:

$ rg ':E:' atari.log | cut -d' ' -f 5,6 | gnuplot -e 'set terminal "dumb"' -e "plot '< cat' notitle with lines"

$ rg ':E:' atari.log | cut -d' ' -f 5,6 | gnuplot -e 'set xlabel "Evaluations"' -e 'set ylabel "Score"' -e 'set terminal "png"' -e 'set output "progress.png" ' -e "plot '< cat' notitle with lines"

In a loop running a full terminal plot every 10 minutes:

while true; do clear ; grep  ':E:' atari.log | cut -d' ' -f 5,6 | gnuplot -e 'set terminal "dumb" size `tput cols` `tput lines`' -e "set yrange [0:1800]"  -e "plot '< cat' notitle with lines" ; sleep 600 ; done

Two y-axis in Gnuplot

gnuplot> plot "foo.txt" u 1:2 with lines axis x1y1, "foo.txt" u 1:3 axis x1y2

Adding line numbers to a file

$ cat -n foo.txt > numbered-foo.txt

Converting SVN repositories to GIT

With an uncompressed svndump archive called foo.svndump, import it to an SVN repository called old:

$ svncreate old
$ svnadmin load ./old < foo.svndump

Take a look at the available repositories:

$ svnlook tree -N old

Then convert one, perhaps the wibble repository:

$ git svn clone file:///Users/richard/Downloads/old/wibble/

…which will create a git repository in the wibble folder.

Using cut with tabs

$ cut -f1 -d$'\t' data.tsv > column1.txt

This $'\t syntax is Bash ANSI-C quoting.

Image resizing from the terminal

I used Imagemagik for this, but SO 28489793 is even easier:

$ sips -Z 640 foo.jpg