Random color selection
In my post about hostname-based prompt colors, I suggested a fallback color scheme that was obviously wrong in order to remind you to set a color for that host:
This carried with it an implicit assumption: you care what color each
host is assigned. You may instead be happy to assign a random color to
each host. We could use
shuf to generate a random color:
ps1_color="32;38;5;$(shuf -i 0-255 -n 1)"
The problem with this solution is the goal of the recoloring the prompt was not simply to make it more colorful, but for that color to have meaning. We want the color to always be the same for each login to a given host.
One way to accomplish this would be to use that code to randomly generate colors, but save the results in a table like the one used before for manually-chosen colors. But it turns out we can do better.
Hash-based color selection
Hash functions have a useful property called determinism, which means that hashing the same value will always get the same result. The consequence is that we can use a hash function like it's a lookup table of random numbers shared among all of our computers:
ps1_color="32;38;5;$(($(hostname | sum | cut -f1 -d' ' | sed s/^0*//) % 256))"
$((...)) syntax is
bash's replacement for
which is less portable but easier to use. Here we use it to make
sure the hash value we compute is a number between 0 and 255.
sum][sum] computes a hash of its input, in this case the result
hostname. Its output is not just a number so
out the number and
sed gets rid of any leading zeros so it isn't
misinterpreted as octal.
The idea of using
sum was suggested by a friend after reading
my previous post on the topic.
But this turns out to not work great for hosts with similar names
Similar colors on hosts with very different names would not be so bad,
but because of how
sum works, it will tend to give similar results
on similar strings (although less often than I expected; it took some
effort to find such an example).
Better hash functions
While this is not a security-critical application, here
cryptographic hash functions solve the problem. Cryptographic
hash functions guarantee (in theory) that knowing that two inputs are
similar tells you nothing about their hash values. In other words,
the output of cryptographic hash functions are indistinguishable
from random and, in fact, they can be used to build
pseudorandom generators like Linux's
The cryptographic hash function utilities output hex instead of decimal,
so they aren't quite a drop-in replacement for
ps1_color="32;38;5;$((0x$(hostname | md5sum | cut -f1 -d' ' | tr -d '\n' | tail -c2)))"
Here we use
tr to select just the hex string of the
-c option specifies the number of bytes to read
from the end, where 2 bytes corresponds to 2 hex digits, which can have
a value of 0 to 255, so the modulo operation is not needed. Instead the
0x prefix inside
$((...)) interprets the string as a hex number and
outputs it as a decimal number.
This code uses the
md5sum utility to compute an
MD5 hash of the hostname. This is recommended because
md5sum is likely to be available on all hosts. Do be aware that
MD5 is insecure and it is only okay to use here because
coloring the prompt is not a security-critical application.
sha256sum are also likely available on modern
systems and work as drop-in replacements for
md5sum in the above
command should you wish to use a different hash. Additionally, you could
also get different values out of the hash by adding a salt:
salt="Some string." ps1_color="32;38;5;$((0x$( (echo "$salt"; hostname) | sha256sum | cut -f1 -d' ' | tr -d '\n' | tail -c2)))"