Installing things with style
Brett Weir, March 27, 2023
If you don't know that it exists, the
coreutils
install
command is
incredibly easy to overlook. I mean, really, what could the install
command
possibly do? Copy files? There's already cp
for that.
Well, it turns out, there's a lot more that a command that copies files can do, and by learning this one weird command, you can save a lot of typing and express, in one command, ideas that would normally take several.
The install
command
Your first, and perhaps only, encounter with the install
tool (unless you're a
package maintainer) is likely when following installation instructions for
single binary software distributions.
Let's say we want to install kubectl
. We've chosen the single binary
installation path to get it onto our machine:
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 138 100 138 0 0 916 0 --:--:-- --:--:-- --:--:-- 920
100 45.8M 100 45.8M 0 0 17.6M 0 0:00:02 0:00:02 --:--:-- 20.8M
So how do I get the binary installed? The first guess would be to use the cp
command:
sudo cp -f kubectl /usr/local/bin/
But there's also the install
command:
sudo install kubectl /usr/local/bin/
At this point, using install
feels identical to cp
, but bear with me,
because it gets better.
Set owner / group
Often, when adding things to a filesystem, you want them to be owned by a
specific user. Usually this is root
, but it could be another login user, a
system user, or something weird like nobody
.
A first attempt to set the owner would probably be:
sudo cp -f kubectl /usr/local/bin/
sudo chown root:root /usr/local/bin/kubectl
But did you know you can use install
, too? install
can take the place of
chown
with the -o
/ --owner
and -g
/ --group
flags:
sudo install -o root -g root kubectl /usr/local/bin/
By default, the above will assign the user or group of the parent process. That
means, in many cases, no extra flags are even needed to get the behavior you
want. That means our install
command can be simplied to the following:
sudo install kubectl /usr/local/bin/
One line instead of two? Not bad, not bad. But install
does more than that.
Set file permissions
Another common task when installing things is setting file permissions. You want your binaries to be executable, right? Maybe not by everyone, but by some people? Or maybe you are copying a text file and want for it to decidedly not be executable, hmm?
To achieve this, let's add chmod
to our previous non-install
command:
sudo cp -f kubectl /usr/local/bin/
sudo chown root:root /usr/local/bin/kubectl
sudo chmod 0755 /usr/local/bin/kubectl
Well, guess what? install
has an app for that. It can take the place of
chmod
with the -m
/ --mode
flag:
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/
By default, install
sets the file permissions to 0755
, which means that it's
readable/executable by everyone, but writable only by the owner. For executable
commands, this is almost always what you want, so for the most common case, no
arguments are needed, and the command can be reduced to:
sudo install kubectl /usr/local/bin/
Now it's one line instead of three. But, again, we're not done yet.
Create parent directories
Up until now, we've been installing into /usr/local/bin/
, which pretty much
always exists. But maybe we want to do something different, like build a root
filesystem or Debian package, and we need to build up the directory structure as
part of our installation.
Say we want to install kubectl
to /opt/custom/path/to/kubectl
instead of our
usual location. If you were to use mkdir
, you'd need to first create
/opt/custom/
then /opt/custom/path/
, and then /opt/custom/path/to/
, in
that order:
sudo mkdir /opt/custom/
sudo mkdir /opt/custom/path/
sudo mkdir /opt/custom/path/to/
To avoid doing all that, you can add the -p
/ --parents
flag to auto-create
parent directories:
sudo mkdir -p /opt/custom/path/to/
So now your complete non-install
command looks something like this:
sudo mkdir -p /opt/custom/path/to/
sudo cp -f kubectl /opt/custom/path/to/
sudo chown root:root /opt/custom/path/to/kubectl
sudo chmod 0755 /opt/custom/path/to/kubectl
install
, on the other hand, offers the -D
option, which behaves just like
the mkdir -p
command above, ensuring kubectl
has a place to go. With
install
, our command becomes the following:
sudo install -D kubectl /opt/custom/path/to/kubectl
Four lines reduced to just one. But that's still not all that install
can do.
Backup existing files
Sometimes, especially on a personal workstation, you might want to keep the old
version of a file around for a little while. Maybe you're upgrading your
kubectl
binary, but aren't quite sure if the new one will work with your
existing cluster.
You might create a little backup of the file first. Let's add the following to
our previous non-install
command:
sudo mkdir -p /opt/custom/path/to/
sudo cp -f /opt/custom/path/to/kubectl /opt/custom/path/to/kubectl~
sudo cp -f kubectl /opt/custom/path/to/
sudo chown root:root /opt/custom/path/to/kubectl
sudo chmod 0755 /opt/custom/path/to/kubectl
With the install
command, you can use the -b
flag, which is much less
verbose:
sudo install -b -D kubectl /opt/custom/path/to/kubectl
Reduce executable size
install
supports stripping debug symbols from executables with the -s
/
--strip
flag. This is often useful when building from source or preparing an
executable for distribution.
Let's assume for the moment that kubectl
still has debug symbols (even though
it doesn't). Our non-install
command adds the strip
command:
sudo mkdir -p /opt/custom/path/to/
sudo cp -f /opt/custom/path/to/kubectl /opt/custom/path/to/kubectl~
sudo cp -f kubectl /opt/custom/path/to/
sudo chown root:root /opt/custom/path/to/kubectl
sudo chmod 0755 /opt/custom/path/to/kubectl
sudo strip /opt/custom/path/to/kubectl
This is getting really unwieldy, but with install
, we just add -s
:
sudo install -s -b -D kubectl /opt/custom/path/to/kubectl
Perform idempotent installations
Idempotence is a fancy way of saying that no matter how many times an action is performed, the result will be the same as if the action had been performed only once.
install
's -c
/ --compare
flag checks file content, ownership, and
permissions. If none of these things have changed, install
will not modify the
target file at all.
This is great! This means you can run your install script as many times as you like and still get the same result.
I don't even want to try the non-install
version, but since you've read this
far, I'd feel bad if I didn't try. Here goes nothing:
# install.sh
if [ -e /opt/custom/path/to/kubectl ] ; then
cmp kubectl /opt/custom/path/to/kubectl
if [ $? -ne 0 ] ; then
sudo mkdir -p /opt/custom/path/to/
sudo cp -f /opt/custom/path/to/kubectl /opt/custom/path/to/kubectl~
sudo cp -f kubectl /opt/custom/path/to/
sudo chown root:root /opt/custom/path/to/kubectl
sudo chmod 0755 /opt/custom/path/to/kubectl
sudo strip /opt/custom/path/to/kubectl
fi
fi
Wow, that's awful! It definitely works, but this is an awful bit of logic to lug around.
With install
, we can turn on idempotent behavior with -c
, and our command
becomes:
sudo install -c -s -b -D kubectl /opt/custom/path/to/kubectl
When all is said and done, this one-liner replaces the entire script I wrote above, and does exactly the same thing, probably faster, and way less prone to typing errors.
Conclusion
The install
command is a fine example of a convenience function. It provides a
terse, easy-to-understand one-liner that is also self-documenting.
Using install
instead of the equivalent individual commands makes the intent
of your installation script very clear, and achieve more precise results with
less effort.
And the best part? install
is always there. Even in a stripped down
busybox
userland, you can expect to find
it, because it's part of coreutils.
So have fun, use it wherever you like, and be one of the cool people who knows
about the ultra-convenient, specialized relative of cp
known as install
.