477 lines
18 KiB
Markdown
477 lines
18 KiB
Markdown
|
---
|
|||
|
author:
|
|||
|
link: https://nickymeuleman.netlify.app/blog/linux-on-windows-wsl2-zsh-docker
|
|||
|
status: not-finished
|
|||
|
date: August 1, 2023
|
|||
|
---
|
|||
|
# WSL2, zsh, and docker. Linux through Windows. | Nicky blogs
|
|||
|
#technology #development #windows subsystem for linux #virtualization #windows 10
|
|||
|
|
|||
|
Compromises are great. When it comes to technology, having your cake and eating it too is better.
|
|||
|
|
|||
|
The machine I normally use for development broke. The [Windows Subsystem for Linux version 2](https://devblogs.microsoft.com/commandline/announcing-wsl-2/) just came out, so I decided to set up another machine with that.
|
|||
|
|
|||
|
When all was said and done, it was nothing short of awesome. I just booted a full-stack application that uses [docker](https://www.docker.com/) from an [Oh My ZSH](https://ohmyz.sh/) terminal window inside of [VSCode](https://code.visualstudio.com/). It booted faster than it ever had natively on Windows.
|
|||
|
|
|||
|
I’m using Windows 10 Home, that means the [hyper-V](https://en.wikipedia.org/wiki/Hyper-V) virtualization technology normally isn’t available. But WSL2 lets you take advantage of it anyway (if your hardware supports it).
|
|||
|
|
|||
|
This post describes the steps I went through to set that up.
|
|||
|
|
|||
|
It’s going to be a long one, so buckle up! 💪
|
|||
|
|
|||
|
## Update Windows 10
|
|||
|
|
|||
|
To use WSL2, Windows 10 has to be updated to version 2004 (Build 10941) or higher.
|
|||
|
|
|||
|
* Run Windows update
|
|||
|
|
|||
|
* Check your Windows version by opening the “Run” dialog (Windows key + R) and entering `winver`.
|
|||
|
|
|||
|
|
|||
|
![Windows version](/static/1da3c5fa193dab10653d3a8e96b2e2ee/b4ffe/winver.png "Windows version")
|
|||
|
|
|||
|
## Activate optional features
|
|||
|
|
|||
|
WSL1 and WSL2 use some features that aren’t activated by default, enabling those is necessary.
|
|||
|
|
|||
|
This is possible through a GUI, by going to “turn Windows features on or off” or through an elevated Powershell prompt.
|
|||
|
|
|||
|
* The GUI option:
|
|||
|
|
|||
|
![Turn Windows features on or off](/static/b2b1235c9f2c4a45c0a68c8f4a0217cb/7fee5/features-search.png "Turn Windows features on or off")
|
|||
|
|
|||
|
To use WSL, enable the aptly named “Windows Subsystem for Linux” feature.
|
|||
|
|
|||
|
![Windows Subsystem for Linux feature](/static/cdaa2ca58b4ce71a62c5934cd792e80b/73926/feature-wsl.png "Windows Subsystem for Linux feature")
|
|||
|
|
|||
|
* Through Powershell:
|
|||
|
|
|||
|
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
|
|||
|
|
|||
|
Regardless of the option you chose, a reboot is required.
|
|||
|
|
|||
|
### Feature for WSL2
|
|||
|
|
|||
|
That was enough for WSL1. To use WSL2, also make sure virtualization is enabled in your BIOS.
|
|||
|
|
|||
|
Enabling virtualization in the BIOS looks different for most motherboard brands.
|
|||
|
On my MSI board the option was called “Virtualization Technology” and was hidden in the overclocking settings.
|
|||
|
|
|||
|
Next: enabling another optional feature in Windows!
|
|||
|
|
|||
|
Same story here, either use the GUI or an elevated Powershell window.
|
|||
|
The option to enable is called the “virtual machine platform”.
|
|||
|
|
|||
|
* GUI
|
|||
|
|
|||
|
![virtual machine platform feature](/static/7f5cc541880a4f778779f24741da3b39/73926/feature-virtual-machine-platform.png "virtual machine platform feature")
|
|||
|
|
|||
|
* Powershell
|
|||
|
|
|||
|
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
|
|||
|
|
|||
|
## Install a distro
|
|||
|
|
|||
|
Once that reboot is done, you can go to the Microsoft store and install your favourite Linux distribution. I went with Ubuntu, because a lot of install instructions and other documentation are written for that distro.
|
|||
|
|
|||
|
![Ubuntu in the Microsoft store](/static/8075506f7745d2c879e8e04d2f98662f/21b4d/store-ubuntu.png "Ubuntu in the Microsoft store")
|
|||
|
|
|||
|
On the first boot of the distro you just installed (which presents itself as a terminal window), you’ll be asked to enter a password for when you want to **do** stuff as a **su**per user (`sudo`).
|
|||
|
|
|||
|
## WSL1 to WSL2
|
|||
|
|
|||
|
To check which version of WSL is installed you can run a command in Powershell.
|
|||
|
|
|||
|
wsl --list --verbose
|
|||
|
|
|||
|
wsl -l -v
|
|||
|
|
|||
|
If the number for the version is 2, all systems go!
|
|||
|
|
|||
|
If not, convert that puppy from 1 to 2.
|
|||
|
|
|||
|
wsl --set-version <distro-name\> 2
|
|||
|
|
|||
|
wsl --set-version Ubuntu 2
|
|||
|
|
|||
|
The output will tell you that this operation might take a while.
|
|||
|
It’s not one of those that says that only to finish 5 seconds later. It took about 10 minutes here.
|
|||
|
|
|||
|
![Converting a distro from WSL1 to WSL2](/static/c9c1e98a28811f3faf6b878e3827e81e/39a20/wsl-set-version.png "Converting a distro from WSL1 to WSL2")
|
|||
|
|
|||
|
At this point, WSL2 is ready to go.
|
|||
|
Opening the distro you installed will show a bash prompt.
|
|||
|
|
|||
|
Windows Subsystem for Linux 2 comes with a real Linux kernel.
|
|||
|
Previously there was a kernel compatibility layer that could not do quite as much.
|
|||
|
|
|||
|
In the words of a certain wooden puppet, it is now a _real boy_.
|
|||
|
|
|||
|
![Pinocchio is a real boy](https://i.imgur.com/fLW42gG.jpg)
|
|||
|
|
|||
|
Users of WSL2 are encouraged to place their files inside the Linux file system.
|
|||
|
That way they benefit from file performance increases compared to WSL1.
|
|||
|
|
|||
|
In WSL2 you can now access files from Linux in Windows and the other way around.
|
|||
|
Modifying Linux files from Windows in WSL1 was always warned against, as this could cause _bad things_ to happen. Whoooo, spooky 👻 (but, really, it was a bad idea.)
|
|||
|
|
|||
|
More details on changes are available in Microsoft’s [WSL2 release blogpost](https://devblogs.microsoft.com/commandline/wsl-2-is-now-available-in-windows-insiders/)
|
|||
|
|
|||
|
Let’s start loading up our environment with some needed tools. A few tools still need to be installed on the Windows side, the rest are all Linux tools.
|
|||
|
|
|||
|
The two I’ll install for Windows are: [VSCode](https://code.visualstudio.com/) and [git for Windows](https://git-scm.com/). Remember to set the autocrlf setting to input for git. VSCode handles it well.
|
|||
|
|
|||
|
git config --global core.autocrlf input
|
|||
|
|
|||
|
Before beginning to install Linux tools, we’ll update our already installed packages.
|
|||
|
|
|||
|
sudo apt update
|
|||
|
|
|||
|
sudo apt upgrade
|
|||
|
|
|||
|
More preparation, installing build tools for [node-gyp](https://github.com/nodejs/node-gyp)
|
|||
|
|
|||
|
sudo apt install build-essential
|
|||
|
|
|||
|
### Git
|
|||
|
|
|||
|
This one should also be installed on the Linux side.
|
|||
|
|
|||
|
After installing git, remember to [configure it](/blog/fresh-development-environment-part-2/#configuration).
|
|||
|
Especially setting the autocrlf setting to input is important here.
|
|||
|
|
|||
|
git config --global core.autocrlf input
|
|||
|
|
|||
|
### Node & NPM
|
|||
|
|
|||
|
You can install it as a standalone package.
|
|||
|
|
|||
|
Now we can harness all those Linux-y tools, I’ll use nvm instead to make using different versions easier.
|
|||
|
|
|||
|
[The nvm repo](https://github.com/nvm-sh/nvm) has excellent installation instructions.
|
|||
|
|
|||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
|
|||
|
|
|||
|
Restart your terminal after the install.
|
|||
|
To confirm the installation was successfull:
|
|||
|
|
|||
|
It should return: `nvm`
|
|||
|
That’s all, no version number, just that string.
|
|||
|
|
|||
|
To install the latest the latest stable version of node:
|
|||
|
|
|||
|
When node releases a new version, you can run that same command again.
|
|||
|
|
|||
|
You’ll need to tell nvm which version of node you want to use.
|
|||
|
So next time you boot your Linux distro, you’ll have to use.
|
|||
|
|
|||
|
When the project you are working on requires a different version of node, specify that one.
|
|||
|
|
|||
|
nvm use v<version number\>
|
|||
|
|
|||
|
nvm use
|
|||
|
|
|||
|
Having to do that manually seems annoying right?
|
|||
|
Many solutions to this annoyance exist. Later in this post that annoyance will be dealt with.
|
|||
|
|
|||
|
### Docker
|
|||
|
|
|||
|
* Kick things off by updating the packages index and installing dependencies.
|
|||
|
|
|||
|
sudo apt update
|
|||
|
|
|||
|
sudo apt install apt-transport-https ca-certificates curl software-properties-common
|
|||
|
|
|||
|
* Add Dockers’s official GPG-key.
|
|||
|
|
|||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
|
|||
|
|
|||
|
* Verify this by running:
|
|||
|
|
|||
|
sudo apt-key fingerprint 0EBFCD88
|
|||
|
|
|||
|
You should see the full key in the output.
|
|||
|
|
|||
|
9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
|
|||
|
|
|||
|
* Add the Docker repository to your list of repositories.
|
|||
|
|
|||
|
sudo add-apt-repository "deb \[arch=amd64\] https://download.docker.com/linux/ubuntu $(lsb\_release -cs) stable"
|
|||
|
|
|||
|
* Update the list of repositories again and install Docker CE.
|
|||
|
|
|||
|
sudo apt update
|
|||
|
|
|||
|
sudo apt install docker-ce
|
|||
|
|
|||
|
Normally, the docker engine starts automatically after the install.
|
|||
|
For me that was not the case so I started it manually.
|
|||
|
|
|||
|
sudo service docker start
|
|||
|
|
|||
|
* Verify Docker CE was installed correcly by booting up their hello-world container.
|
|||
|
|
|||
|
sudo docker run hello-world
|
|||
|
|
|||
|
### Docker Compose
|
|||
|
|
|||
|
As a handy tool for managing docker containers, `docker-compose` is frequently installed alongside `docker-ce`.
|
|||
|
|
|||
|
* Download the current stable release and place it in the `/usr/local/bin` folder.
|
|||
|
|
|||
|
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)\-$(uname -m)" -o /usr/local/bin/docker-compose
|
|||
|
|
|||
|
* Make the file executable.
|
|||
|
|
|||
|
sudo chmod +x /usr/local/bin/docker-compose
|
|||
|
|
|||
|
* Verify the installation.
|
|||
|
|
|||
|
### Docker Desktop
|
|||
|
|
|||
|
A short trip back to the Windows side!
|
|||
|
|
|||
|
The previous way to _do Docker-y things_ all happened through the Linux terminal. While this is fine, the [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop) application integrates with WSL2 quite well and provides a GUI.
|
|||
|
|
|||
|
![Installing the Docker Desktop application](/static/74dc836f010ba50ca452b2339b374969/5ebd7/docker-desktop-install.png "Installing the Docker Desktop application")
|
|||
|
|
|||
|
If you start a docker container in your (Linux) terminal, it will appear in, and can be controlled through the interface.
|
|||
|
|
|||
|
e.g. Running the getting-started docker container in the Linux terminal
|
|||
|
|
|||
|
docker run -dp 80:80 docker/getting-started
|
|||
|
|
|||
|
will make the container appear in the interface, where it can be opened in a browser, stopped, restarted, …
|
|||
|
|
|||
|
![The container we started appears in the interace.](/static/39cf09a4af2da9886d46dc916e2912b1/1ff84/docker-desktop-dashboard.png "The container we started appears in the interace.")
|
|||
|
|
|||
|
### Yarn
|
|||
|
|
|||
|
[Yarn](https://yarnpkg.com/) is a package manager for JavaScript made by Facebook.
|
|||
|
|
|||
|
* Add their gpg key.
|
|||
|
|
|||
|
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
|||
|
|
|||
|
* Add their repository.
|
|||
|
|
|||
|
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
|||
|
|
|||
|
* Update the list of repositories and install Yarn.
|
|||
|
|
|||
|
sudo apt update
|
|||
|
|
|||
|
sudo apt install --no-install-recommends yarn
|
|||
|
|
|||
|
* Verify the installation.
|
|||
|
|
|||
|
### Visual Studio Code Remote - WSL extension
|
|||
|
|
|||
|
The [Visual Studio Code Remote - WSL](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) extension will let you use [VSCode](https://code.visualstudio.com/) on Windows to interact with WSL. You can develop entirely in the Linux environment, while viewing the results in Windows.
|
|||
|
|
|||
|
Below is a screenshot of me writing this blogpost in VSCode, while connected to my Ubuntu installation. Notice the `WSL: Ubuntu` badge in the lower left corner.
|
|||
|
|
|||
|
![Me writing this blogpost in VSCode](/static/fbd80cb0ea37608b6e789fa1401a72e0/68947/vscode-remote-wsl.png "Me writing this blogpost in VSCode")
|
|||
|
|
|||
|
## A better terminal
|
|||
|
|
|||
|
When you open your terminal there are 2 sides at work.
|
|||
|
The thing you see and type into, **the terminal**.
|
|||
|
The thing that terminal communicates with and does most of the work, **the shell**.
|
|||
|
|
|||
|
Let’s face it, the terminal that ships with Windows is pretty lackluster.
|
|||
|
Good news: they are ~coming~ came out with [a new one](https://youtu.be/8gw0rXPMMPE) and it looks really awesome!
|
|||
|
|
|||
|
~In anticipation of that release, I’m not going to change much there. I’ll use the one that’s integrated into VSCode most of the time anyway.~
|
|||
|
|
|||
|
### Change integrated VSCode terminal
|
|||
|
|
|||
|
VSCode’s integrated terminal is a great productivity booster.
|
|||
|
Let’s use the brand new Linux shell in there.
|
|||
|
|
|||
|
When opening the integrated terminal, you can choose wich one to use as the default one.
|
|||
|
Select WSL from the resulting list of options and you are done!
|
|||
|
|
|||
|
![select terminal in Visual Studio Code](/static/650283d5ed274f48f93fc136b42a008b/57dc1/terminal-select.png "select terminal in Visual Studio Code")
|
|||
|
|
|||
|
Alternatively, edit the `settings.json` to point to the correct location.
|
|||
|
|
|||
|
"terminal.integrated.shell.linux": "/bin/sh",
|
|||
|
|
|||
|
### ZSH
|
|||
|
|
|||
|
I chose [ZSH](https://en.wikipedia.org/wiki/Z_shell) to replace the standard bash shell that opens when clicking the Ubuntu icon in the start menu.
|
|||
|
|
|||
|
Installing it is a oneliner thanks to the package manager in Ubuntu.
|
|||
|
|
|||
|
When you launch Ubuntu, you’ll still see the usual bash prompt.
|
|||
|
To start the zsh shell from that familiar bash prompt, enter:
|
|||
|
|
|||
|
The first time this launches, a configuration wizard will be shown.
|
|||
|
The choice made here doesn’t matter all that much, since the resulting file `.zshrc` file will be overwritten when we install [oh-my-zsh](https://ohmyz.sh/) a bit later. I chose the option `2` anyway and went with the defaults.
|
|||
|
|
|||
|
![zsh-install](/static/35e6e0a23d0b3b43a4bc3572ea4026f6/adc48/zsh-install.png "zsh-install")
|
|||
|
|
|||
|
Typing `zsh` into bash every time we launch it gets old quick.
|
|||
|
Time to change the default shell to zsh!
|
|||
|
|
|||
|
_Thanks to Reddit user_ [bhramchari](https://www.reddit.com/r/bashonubuntuonwindows/comments/c1fy1c/stock_win10_to_wsl2_ubuntu_with_zsh_and_docker/erdckwe/) _for suggesting this method!_
|
|||
|
|
|||
|
### Oh My ZSH
|
|||
|
|
|||
|
This extension to zsh has one of the best URLs out there: [ohmyz.sh](https://ohmyz.sh/)
|
|||
|
It will also enable a huge list of nice features, which is more important.
|
|||
|
|
|||
|
Installing oh-my-zsh is a oneliner.
|
|||
|
|
|||
|
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
|
|||
|
|
|||
|
This will also replace that `.zshrc` file we initialised earlier.
|
|||
|
|
|||
|
Enjoy the cool ASCII art signalling a successful installation!
|
|||
|
|
|||
|
#### Theme oh-my-zsh
|
|||
|
|
|||
|
oh-my-zsh comes with lots of fancy themes, so let’s install a pretty one!
|
|||
|
Take a look at [a list of them](https://github.com/robbyrussell/oh-my-zsh/wiki/Themes).
|
|||
|
|
|||
|
I like the agnoster one, so that’s what I’ll go with.
|
|||
|
|
|||
|
Edit the `.zshrc` file to enable it.
|
|||
|
This file is also located in your Linux home directory (`cd $home` to go there).
|
|||
|
|
|||
|
.zshrc
|
|||
|
|
|||
|
ZSH\_THEME\="robbyrussell"
|
|||
|
|
|||
|
ZSH\_THEME\="agnoster"
|
|||
|
|
|||
|
restart your terminal for it to take effect.
|
|||
|
|
|||
|
#### Powerline fonts
|
|||
|
|
|||
|
Aaaaaaaaaah, it’s … broken. 😥
|
|||
|
|
|||
|
![ugly oh my ZSH](/static/4eab3fb6a81577500c2e25d8660f6bbe/cd138/ugly-oh-my-zsh.png "ugly oh my ZSH")
|
|||
|
|
|||
|
That’s partly because this is a fancy prompt that needs a [Powerline-patched font](https://github.com/powerline/fonts) to render correctly.
|
|||
|
|
|||
|
You can either download the specific font you want and install it that way (double clicking on the file and hitting the install button), or use the install script to install them all at once.
|
|||
|
|
|||
|
To install them all at once first clone that repo.
|
|||
|
Then open an elevated Powershell window.
|
|||
|
To be able to execute the `install.ps1` file, we need to open up the execution policy temporarily.
|
|||
|
|
|||
|
Set-ExecutionPolicy Bypass
|
|||
|
|
|||
|
Now navigate to the cloned repo and execute the install script.
|
|||
|
|
|||
|
You’ll see the same thing happening as you would when downloading and installing each font seperatly, but of course much quicker.
|
|||
|
|
|||
|
Set-ExecutionPolicy Default
|
|||
|
|
|||
|
After installing the fonts, setting them in your terminal application changes the weird boxes to icons.
|
|||
|
|
|||
|
Note that the standard terminal in Windows still doesn’t work quite right after that. It has many limitations, this not working completely is just one of them.
|
|||
|
As stated before, I’m mainly going to use the terminal inside VSCode, so I’m leaving the standard terminal behind.
|
|||
|
|
|||
|
After setting the terminal font in VSCode, all icons show up beautifully.
|
|||
|
|
|||
|
The colors when listing directories and files with the `ls` command still look, euuuhm, suboptimal.
|
|||
|
|
|||
|
![ugly dircolors](/static/2a019f31f94b76753aa42751b8ffa162/6c2f2/ugly-dircolors.png "ugly dircolors")
|
|||
|
|
|||
|
#### dircolors
|
|||
|
|
|||
|
To replace those ugly colors, something called dircolors may be used.
|
|||
|
I’ll install the popular [solarized dircolors](https://github.com/seebi/dircolors-solarized)
|
|||
|
|
|||
|
Pick the theme you want and download the file to your home directory.
|
|||
|
|
|||
|
curl https://raw.githubusercontent.com/seebi/dircolors-solarized/master/dircolors.ansi-dark --output ~/.dircolors
|
|||
|
|
|||
|
Add a line to the bottom of your zsh configuration file to use what you just downloaded.
|
|||
|
|
|||
|
eval \`dircolors ~/.dircolors\`
|
|||
|
|
|||
|
Much better, it looks great now!
|
|||
|
|
|||
|
![pretty-terminal](/static/dcdbda00edb6e5b4e4f2fcac9d301b7c/2a50c/pretty-terminal.png "pretty-terminal")
|
|||
|
|
|||
|
### Plugins for oh-my-zsh
|
|||
|
|
|||
|
By itself, oh-my-zsh is already feature-rich. For further productivity increases, plugins are there to help.
|
|||
|
|
|||
|
Many of those plugins ship alongside oh-my-zsh, making installing them as easy as adding a line to `.zshrc`.
|
|||
|
Others require a bit more effort, let’s start with those!
|
|||
|
|
|||
|
#### Syntax Highlighting
|
|||
|
|
|||
|
[zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting) is a handy plugin that prevents syntax errors by highlighting valid commands in green and invalid ones in red.
|
|||
|
|
|||
|
To install the plugin first clone [the repo](https://github.com/zsh-users/zsh-syntax-highlighting).
|
|||
|
Then add the script to your `.zshrc`.
|
|||
|
|
|||
|
.zshrc
|
|||
|
|
|||
|
source <path-to-the-script\>
|
|||
|
|
|||
|
source ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
|
|||
|
|
|||
|
#### Auto Suggestions
|
|||
|
|
|||
|
[zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) makes typing the same command over and over much quicker by suggesting what you could type next. These suggestions are based on command history.
|
|||
|
|
|||
|
Installing it follows the same pattern as the plugin we installed above.
|
|||
|
|
|||
|
First clone the repo, then add a line to `.zshrc`
|
|||
|
|
|||
|
.zshrc
|
|||
|
|
|||
|
source <path-to-the-script\>
|
|||
|
|
|||
|
source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh
|
|||
|
|
|||
|
#### pre-installed plugins
|
|||
|
|
|||
|
A list of the plugins that ship with oh-my-zsh can be viewed on this [Github page](https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins).
|
|||
|
|
|||
|
Each one should have a `README.md` that tells you more about what it does and how to activate the plugin.
|
|||
|
Most of the time, activating the plugin requires you to add it to the list of plugins in `.zshrc`.
|
|||
|
|
|||
|
This is what I ended up with:
|
|||
|
|
|||
|
.zshrc
|
|||
|
|
|||
|
plugins\=(
|
|||
|
|
|||
|
git
|
|||
|
|
|||
|
node
|
|||
|
|
|||
|
npm
|
|||
|
|
|||
|
npx
|
|||
|
|
|||
|
nvm
|
|||
|
|
|||
|
z
|
|||
|
|
|||
|
)
|
|||
|
|
|||
|
The plugin I would like to call out here is [z](https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins/z).
|
|||
|
It make navigating to _frecent_ folders easy.
|
|||
|
|
|||
|
You read that right, it’s not a typo.
|
|||
|
It’s a contraction of frequent and recent.
|
|||
|
|
|||
|
For example, my blog is located at `~/projects/nicky-blog`
|
|||
|
If I type `z blog`, z will take me there.
|
|||
|
|
|||
|
## Result
|
|||
|
|
|||
|
Whew! That was quite the journey 😅💦
|
|||
|
The result is very powerful, and it looks good too! 🎊🎉🎉🎉🎊
|
|||
|
|
|||
|
![the end result](/static/3ad6eb8e76c6317b9e71e75e61562896/bb3b7/end-result.png "the end result")
|