opscape

Files and directories

install

Copy files into place and set their mode, owner and group in one step; can also create directories.

install [OPTION...] SOURCE... DEST | install -d [OPTION...] DIRECTORY...

9 of 9 examples run in Debian 13.7 on 2026-10-31 · coreutils 9.7-3 · manual: install(1)

Despite the name, install has nothing to do with package managers. It is cp plus chown plus a permission change, done in one command: copy this file there, with this mode, owned by this user. Makefiles use it in their install targets, and deployment scripts use it to put configuration files and binaries in place with exactly the permissions they need.

Two details make it different from cp. The default mode is 755 (rwxr-xr-x), not the source's mode, so pass -m 644 for non-executable files. And it replaces the destination by removing it first instead of writing into it, so a program that is running from the old file keeps working.

install -d creates directories (with parents) and sets their mode and owner, like mkdir -p with extra options. -D creates the destination's missing parent directories before copying.

Examples

Install a script as an executable

mkdir -p bin
install scripts/greet.sh bin/greet
ls -l bin/greet
bin/greet installer
output
-rwxr-xr-x 1 dev dev 37 Oct 31 20:19 bin/greet
Hello, installer!

The file is copied and given mode 755 (-rwxr-xr-x), regardless of the source's mode, so it runs straight away.

Install a config file with a specific mode

mkdir conf
install -m 644 data/settings.conf conf/
install -m 600 .env conf/
ls -lA conf
output
total 1
-rw------- 1 dev dev  42 Oct 31 20:19 .env
-rw-r--r-- 1 dev dev 149 Oct 31 20:19 settings.conf

-m sets the mode. Plain data gets 644, and a file holding secrets gets 600, readable only by the owner.

Create directories with a given mode

install -d -m 750 deploy/releases deploy/shared/logs
ls -ld deploy/releases deploy/shared deploy/shared/logs
output
drwxr-x--- 2 dev dev 2 Oct 31 20:19 deploy/releases
drwxr-xr-x 3 dev dev 3 Oct 31 20:19 deploy/shared
drwxr-x--- 2 dev dev 2 Oct 31 20:19 deploy/shared/logs

-d treats the arguments as directories and creates missing parents. The mode applies to the named directories; parents created on the way (deploy/shared) get the default mode.

Copy into a path that does not exist yet

install -D -m 644 src/lib/helpers.py dist/lib/python/helpers.py
find dist
output
dist
dist/lib
dist/lib/python
dist/lib/python/helpers.py

-D creates every missing directory leading up to the destination, then copies the file.

Set owner and group while copying

Setup run before this example
useradd -M -U -r app
install -o app -g app -m 640 data/config.json /etc/demo-config.json
ls -l /etc/demo-config.json
output
-rw-r----- 1 app app 244 Oct 31 20:19 /etc/demo-config.json

-o and -g set the owner and group (this needs root). One command replaces the usual cp, chown and chmod sequence, with no moment where the file has the wrong owner.

Show what is installed where

mkdir -p out
install -v -m 644 data/*.csv out/
output
'data/sales.csv' -> 'out/sales.csv'
'data/users.csv' -> 'out/users.csv'

-v prints each copy. With several sources, the destination must be a directory.

Keep the previous version

Setup run before this example
mkdir -p bin
install scripts/greet.sh bin/greet
install -b -S .old scripts/backup.sh bin/greet
ls bin
output
greet
greet.old

-b renames the file being replaced, and -S .old sets the suffix (the default is ~).

Skip the copy when nothing changed

Setup run before this example
mkdir -p bin
install -m 755 scripts/greet.sh bin/greet
touch -d "2026-01-01" bin/greet
install -C -m 755 scripts/greet.sh bin/greet
ls -l --time-style=+%F bin/greet
output
-rwxr-xr-x 1 dev dev 37 2026-01-01 bin/greet

-C compares content, owner and mode first and leaves an identical destination alone, so its old timestamp survives and tools that watch modification times see no change.

install replaces the file, cp overwrites it in place

Setup run before this example
mkdir -p bin
install scripts/greet.sh bin/greet
ls -i bin/greet
install scripts/greet.sh bin/greet && ls -i bin/greet
cp scripts/greet.sh bin/greet && ls -i bin/greet
output
9811877 bin/greet
9466071 bin/greet
9466071 bin/greet

Each install produces a new file with a new inode number; cp wrote into the existing one, so the number stayed. A program running from the old file keeps its own copy with install, while overwriting a running binary in place with cp can fail with "Text file busy" or crash it. In Makefiles, install -m 755 prog $(DESTDIR)$(PREFIX)/bin/ is the standard install step for this reason.

Common pitfalls

  • install file dest makes dest executable (mode 755) even for a text file. Always pass -m 644 for data and config files.

  • install follows symlinks in the source and copies the target file; it cannot install a symlink.

  • With one source and a destination that is an existing directory, the file goes inside it. Use -T to treat the destination as a file name and fail otherwise.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • -D, -t, -T and -v are GNU options. BSD/macOS install shares -d, -m, -o, -g, -b, -C and -c (which is a no-op on GNU, kept for compatibility).

Options worth knowing

A selection, described in our own words. For the full list, see install(1) or install --help.

OptionWhat it does
-m, --mode MODESet the permission mode (default 755).
-o, --owner OWNERSet the owner (root only).
-g, --group GROUPSet the group.
-d, --directoryTreat all arguments as directories to create.
-DCreate missing parent directories of the destination.
-C, --compareDo not replace a destination that is already identical.
-bBack up each existing destination file.
-S, --suffix SUFFIXUse SUFFIX for backups instead of ~.
-p, --preserve-timestampsKeep the source's access and modification times.
-s, --stripStrip symbol tables from installed binaries.
-t, --target-directory DIRCopy all sources into DIR.
-T, --no-target-directoryTreat DEST as a file name, never a directory.
-v, --verbosePrint each file or directory created.
-cIgnored; accepted for compatibility with old BSD install.

Related