A shell script to install and uninstall dotfiles using symlinks.
stow
written in bash, in other words.
Put stowsh in your path.
stowsh
strives for minimal dependencies, but it currently depends on the GNU
coreutils implementation of realpath
and GNU findutils.
These are already installed on most Linux systems. On macOS you can install
them with brew install findutils coreutils
.
I would welcome PRs to remove these dependencies and replace them with POSIX shell commands!
Create a package (a directory of dotfiles, ~/.dotfiles
in this example):
$ cd
$ tree -a
.
└── .dotfiles
├── .bash_profile
└── .vimrc
Install the package (create symlinks in the current directory) using stowsh
$ stowsh ~/.dotfiles
$ tree -a
.
├── .bash_profile -> .dotfiles/.bash_profile
├── .dotfiles
│ ├── .bash_profile
│ └── .vimrc
└── .vimrc -> .dotfiles/.vimrc
Uninstall the package (delete symlinks) using stowsh
$ stowsh -D ~/.dotfiles
A package is a directory containing related configuration files. stowsh
symlinks the package's contents to the corresponding locations in the current
directory, creating subdirectories as necessary.
$ stowsh -h
Usage: stowsh [-D] [-n] [-s] [-g] [-v[v]] [-t TARGET] PACKAGES...
TARGET
is the destination directory (current directory by default).
-D
uninstall package(s)-n
dry-run (print what would happen, but don't do anything)-v
verbose (-vv
is even more verbose)-g
ignore files that are not tracked by git (usesgit ls-files
)-s
skip (skip errors rather than abort)
When installing a package stowsh
will never overwrite existing files. When
uninstalling a package stowsh
will never delete files that are not symlinks
to the expected place in the package.
By default stowsh
will abort without making any changes if either of these
errors occurs. This is done to avoid being left with a broken half installed
configuration. The -s
flag can be used to force stowsh to skip these errors
and install/uninstall as much as possible.
One important consequence of the fact that stowsh
does not delete files that
aren't symlinks to the right place is (deep breath!): if you install a package,
delete a file from the package, then uninstall the package, your target
directory will be left with a broken symlink to the deleted file.
You can put your dotfiles in one package and install that with stowsh
directly.
Or you may prefer to have multiple orthogonal packages that get installed by a
script that uses stowsh
. This allows you to install packages only if certain
conditions are met. Here's an example install script that uses
stowsh.
If your needs go beyond this, there are many fully-featured dotfiles managers.
stowsh
will only create links to files and links. It does not create links to
directories.
If a package contains directories, and the corresponding directories do not
exist in the target, stowsh
will create real directories (not links).
This choice allows two packages to install files in the same subdirectory, and
files that don't belong in your dotfiles repo (e.g. caches) to be added to
those subdirectories without also being added to your dotfiles repo or its
.gitignore
file.
$ tree -a
.
├── .conf
│ └── conf.cache
└── .dotfiles
├── pkg1
│ ├── .conf
│ │ ├── a.conf
│ │ ├── b.conf
│ │ └── c.conf
│ └── bin
│ └── script1
└── pkg2
└── bin
└── script2
$ stowsh .dotfiles/pkg1 .dotfiles/pkg2
$ tree -a -I '.dotfiles' # exclude ./.dotfiles from tree listing
.
├── .conf
│ ├── a.conf -> ../.dotfiles/pkg1/.conf/a.conf
│ ├── b.conf -> ../.dotfiles/pkg1/.conf/b.conf
│ ├── c.conf -> ../.dotfiles/pkg1/.conf/c.conf
│ └── conf.cache
└── bin
├── script1 -> ../.dotfiles/pkg1/bin/script1
└── script2 -> ../.dotfiles/pkg2/bin/script2
Things to note here:
~/bin
was created bystowsh
. It's a real directory, not a link.- both
pkg1
andpkg2
install files into~/bin
- the directory
.conf
existed beforepkg1
was installed conf.cache
is a real file that exists alongside the symlinks installed bystowsh
When uninstalling a package, subdirectories will only be deleted if they are empty. So:
$ stowsh -D .dotfiles/pkg1 .dotfiles/pkg2
$ tree -a -I '.dotfiles'
.
└── .conf
└── conf.cache
stowsh
is a short, cross-platform bash script without dependencies. stow
is
implemented as a Perl module. I'm not smart enough to install Perl modules, and
I'd rather not have Perl as a dependency.
Mike Lee Williams. Issues and PRs welcome.