Your Terminal and You: Dotfiles
Tags: bash
Published: 2020-11-10 16:29:28

Have you ever watched a skilled engineer pull up a terminal, issue a few keystrokes and kick off a whole series of processes in seconds? It can be downright magical, some even wizard-like. A few are hard to distinguish from cackling necromancers raising processes from the dead.

![Photo by Rhett Wesley / Unsplash](https://images.unsplash.com/photo-1502919280275-1bed9aca68ab?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ)

I'll let you in on a little secret, engineers who are comfortable on the command line are not wizards, they are pack rats. They accumulate techniques and store them away in a toolbox and take it everywhere they go.

That engineer can sit down at any machine and within minutes reach into their toolbox and become productive.

Let's take a look at one technique that I use. It is not my technique, like all good pack rats I borrowed it from countless others and added them to my toolbox.

The technique I use, is fairly macOS focused. It will work on macOS, Linux and WSL on Windows. You can do similar things in PowerShell, I encourage you to take a look at [Michael Jolly](https://baldbeardedbuilder.com/)'s [devtoolbox](https://github.com/builders-club/devtoolbox) project for examples.

Let's start by creating your toolbox in a way that you can access it from any machine. Head to [GitHub](https://github.com/new) and create a new repository called **dotfiles**. This is a common name for this kind of repository, remember it. You can find all kinds of great examples by looking at other peoples dotfiles repos.

![Recommended settings for new dotfiles repository](https://s3-us-west-1.amazonaws.com/iamnotmyself-com/2020/11/Screen-Shot-2020-11-09-at-8.54.01-AM.png)

If you want to allow others to use tools from your toolbox, consider adding an MIT license and making your repo public. Be sure to add a README file. It will be useful later when you want to start documenting things for others to consume.

Once you have created the repository, let's clone it to your machine. We will do this in a special place called your home directory located in your user directory. We will also give it a special name.

```bash
cd ~
git clone git@github.com:NotMyself/dotfiles.git .dotfiles
```
Use git to clone the repository to a directory named **.dotfiles**. Notice the period in front of the directory name. This makes the directory a hidden one. Trying running these two commands to see what I mean.

```bash
ls
ls -a
```
The first command lists the files and directories in the current directory. Notice that the **.dotfiles** directory is not displayed. The second command uses the -a flag to show all files and directories.

Here is what it looks like on my machine currently.

![running ls vs. ls -a](https://s3-us-west-1.amazonaws.com/iamnotmyself-com/2020/11/Screen-Shot-2020-11-09-at-9.31.50-AM.png)

As you can see, I have a variety of dot files for various things. Can you spot my **.dotfiles** directory?

Next, you will need to wire up your **.dotfiles** directory in a way that your terminal application will use it. By default, macOS terminal uses [zsh](http://zsh.sourceforge.net/) as it's shell. So create a **zsh** directory in the **.dotfiles** directory and add a **.zshrc** file to it.

```bash
cd ~/.dotfiles
mkdir zsh
touch ./zsh/.zshrc
```

When a new shell is started in a terminal application, **zsh** will look for a **.zshrc** file in the home directory of the current user. It will then execute that file. Of course, your file is located in the **.dotfiles/zsh** directory.

We can fix that by creating a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) between the file's current location on disk and where **zsh** expects it to be. To do that, create an **install** script in your **.dotfiles** directory.

```bash
echo "#\!/usr/bin/env sh" > ./install
echo "echo \"Installing..\"" >> ./install
chmod +x ./install
./install
```

There is a bit of magic going on here, so let's break it down. The first command creates a file named **install** and adds a special value as the first line. This value is called a **shebang**. It is used to decide how a file will be executed. In this case, the shebang checks the local system environment for the configured shell. So this install script will execute in **zsh**.

The next command, adds a line to the install file that will write the text **"Installing..."** to the terminal when the install script is executed. We will use this to verify everything is working correctly.

Text files in macOS are not typically executable. So the next command explicitly set the install text file as executable. This only needs to be done once when you create a script file.

The final command executes the install script. The output on my machine looks like this.

![Running the install script](https://s3-us-west-1.amazonaws.com/iamnotmyself-com/2020/11/Screen-Shot-2020-11-09-at-10.03.15-AM.png)

Finally, add the command needed to create the symbolic link to the .zshrc file to the install script. Open the install script in a text editor and add the following line.

```bash
# shell settings
ln -sv /Users/bobby/.dotfiles/zsh/.zshrc ~/.zshrc
```
Note that your user directory name will be different than mine. Be sure to change the **bobby** value to the name of your user directory. If you are not sure what that name is run `echo ~` in your terminal to display the full path to your home directory.

This is a key concept in creating your toolbox. Instead of just looking up commands and running them once to set things up make a script file that does it so you do not have to look it up again.

I simply looked at my [install script](https://github.com/NotMyself/dotfiles/blob/master/install) in my [dotfiles](https://github.com/notmyself/dotfiles) repository to find the symlink command. See, I am not a wizard I am a pack rat.

At this point you should commit your changes to the Git repository. Now, run the install script.

```bash
./install
```
You may receive an error that a file named .zshrc already exists in your home directory. In that case, make a backup of that file for later review and run the install again.

```bash
mv ~/.zshrc ~/.zshrc_backup
./install
```
You can now list the contents of your home directory and you should see the **.zshrc** file.

```bash
ls -a ~
```
Remember though, this is a symbolic link to the file in our **.dotfiles** repository. Which basically makes it our entry point to setup our shell any way we want.

Now, let's make it do something interesting. Open the **~/.dotfiles/zsh/.zshrc** file in a text editor and add the following.

```bash
for DOTFILE in `find /Users/bobby/.dotfiles/system`
do
  [ -f $DOTFILE ] && source $DOTFILE
done
```

This script will execute each of the files located in the **system** directory in our **.dotfiles** directory. Making them available to the current shell.

I typically setup a series of files in the system folder: **.alias**, **.env**, **.function**, **.path**, and **.prompt**.

- .alias - contains a set of command aliases to commands that already exist on my machine.
- .env - contains commands to add additional functionality into my terminal environment.
- .function - contains a set of functions that are useful in my terminal environment.
- .path - updates the $PATH to include any other binary sources on my machine.
- .prompt - contains the configuration needed to setup my fancy shell prompt.

Aliases can be very useful for common commands containing elaborate flags and values that you use regularly. Let's set up a few so you can see how easy it is. First, we need to set up the **.alias** file.

```bash
cd ~/.dotfiles
mkdir system
touch system/.function
```
Then add the following code to the **.alias** file.

```bash
alias l="ls -la"       # List in long format, include dotfiles
alias ld="ls -ld */"   # List in long format, only directories
alias ..="cd .."
alias ...="cd ../.."
alias ....="cd ../../.."
```
These aliases are ones that I use for navigating around the file system. As you can see they are simply shortcuts for other commands.

Functions on the other hand allow you to do more complex tasks with a single command. Let's set you up with one of my favorite functions. Typically when I create a new directory using the **mkdir** command the very next thing I do is change directories into the one I just created.

Let's create a function that does this in one command instead of two. First, you need to create the **.function** file.

```bash
cd ~/.dotfiles
touch system/.function
```

Next, add the following code to the .function file using a text editor.

```bash
# Create a new directory and enter it
function mk() {
  mkdir -p "$@" && cd "$@"
}
```
Now when you wish to create a directory and enter it you can execute the command `mk foo`.

Commit these changes to your **dotfiles** repository.

And with that your toolbox is setup. Start collecting the tools that make you look like a wizard. You can take a look at my [dotfiles](https://github.com/NotMyself/dotfiles) repository for ideas. Or, search [GitHub](https://github.com/search?q=dotfiles&type=repositories) for others.