Installing things with style
The install command is profoundly useful and profoundly underrated. Learn how this tiny unassuming command can save a ton of scripting and make your installations cleaner and more robust.
Brett Weir, Mar 27, 2023, 6 mins
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.