8 tips for setting up PowerShell on Windows
Tags: productivity, tutorial, beginners, powershell
Published: 2022-04-15 11:02:55

There are some tools and programs that allow you to get more out of Windows command line, making experience and workflows more fluid. With just few added tools we can improve folder navigation, searching through files and git operations without memorizing a lot of different commands. Also, all tools used here are available in Linux environments (and with that in WSL) which makes switching between WSL instance and Windows much easier. 

{% collapsible Table of Contents %}
- [1. Prerequisites](#1-prerequisites)
  - [1.1. Windows Terminal](#11-windows-terminal)
  - [1.2. PowerShell](#12-powershell)
- [2. Tools](#2-tools)
  - [2.1. Scoop](#21-scoop)
  - [2.2. fd](#22-fd)
  - [2.3. fzf](#23-fzf)
  - [2.4. lazygit](#24-lazygit)
  - [2.5. lazydocker](#25-lazydocker)
  - [2.6. ripgrep](#26-ripgrep)
  - [2.7. ZLocation](#27-zlocation)
  - [2.8. PSFzf](#28-psfzf)
- [3. PowerShell profile setup](#3-powershell-profile-setup)
- [4. Usage with fzf](#4-usage-with-fzf)
- [5. Conclusion](#5-conclusion)
{% endcollapsible %}

## 1. Prerequisites

### 1.1. Windows Terminal

We'll use Windows Terminal but it's not mandatory. Any other terminal emulator will do, but Windows Terminal has risen in popularity lately. 

Install and download from here [https://github.com/microsoft/terminal#installing-and-running-windows-terminal](https://github.com/microsoft/terminal#installing-and-running-windows-terminal)

Recommended way is through Windows Store to get automatic updates.

### 1.2. PowerShell

To download PowerShell visit [https://aka.ms/powershell-release?tag=stable](https://aka.ms/powershell-release?tag=stable) and update to the latest version. All steps here shown are using PowerShell 7.1.3. After installation you should see PowerShell in the Windows Terminal dropdown list. It's recommended to use this as default shell.

You can check installed version with:

```powershell
$PSVersionTable | Select-Object PSVersion
```

## 2. Tools

Open up Windows Terminal, select PowerShell and follow along. Each tool has extensive documentation with more examples. I'll offer just few, but be sure to check them out in detail.

### 2.1. Scoop

[Scoop](https://scoop.sh/) is awesome tool to get started with. It can be viewed as alternative to [Chocolatey](https://chocolatey.org/), however Scoop is more oriented towards developer command line tools and their installation process.

To install it, and add `extras` bucket, use this:

```powershell
# Download installer script and run it
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')

# Add 'extras' bucket (has other useful tools)
scoop bucket add extras

# List all installed apps
scoop list
```

Next in line are tools to install using Scoop:

### 2.2. fd

[fd](https://github.com/sharkdp/fd) is used as alternative to find command. It's ultra fast, allows regex matching, filter by file or directory and respects ignore files (`.ignore`, `.gitignore`, etc...)

```powershell
# Install fd using scoop
scoop install fd

# Search current dir recursively for file containing "index" in the name
fd index

# Search file containing "index" in the name in the "./projects" subfolder
fd index ./projects

# Get all directories recursively
fd -t dll
```

### 2.3. fzf

[fzf](https://github.com/junegunn/fzf) is super fast fuzzy filter/finder. It launches interactive finder with results that are piped into it and will write selected item to `STDOUT`. If not used with pipe, fzf will get the list of files (using find).

There is [search syntax](https://github.com/junegunn/fzf#search-syntax) to narrow down results, so check that out to improve workflow even more.

```powershell
# Install fzf using scoop
scoop install fzf

# Search (using fd) all files with md extension, filter it and open result
# in notepad
fd -e md | fzf | %{ notepad $_ }
```

> 💡 We'll use fzf later with other tools that can use STDOUT and do someting with selected result.


### 2.4. lazygit

[lazygit](https://github.com/jesseduffield/lazygit) is awesome terminal UI for git. It helps with git commands by offering simple UI, menu, interactive rebase, recent branches, scrolling through logs and diffs.

```powershell
# Install lazygit using scoop
scoop install lazygit

# Run it, q to exit
lazygit
```

> 💡 lazygit even supports mouse events (and works in Windows Terminal > v.1.9) which means that you can even click in the tool.


### 2.5. lazydocker

lazydocker comes from the same author as lazygit. It offers similar UI, but for managing Docker. View states, logs, volumes and do common operations (restart, remove, etc...) without memorizing all the params by heart.

```powershell
# Install lazydocker using scoop
scoop install lazydocker

# Run it, q to exit
lazydocker
```

### 2.6. ripgrep

[ripgrep](https://github.com/BurntSushi/ripgrep) is tool used to recursively search current directory primarily for text content (using regex) while respecting ignore files (`.ignore`, `.gitignore`, etc...)

```powershell
# Install ripgrep using scoop
scoop install ripgrep

# Search existing directory for files that contain word "async"
rg async

# Specify file to search in
rg async ./userService.ts
```

### 2.7. ZLocation

[ZLocation](https://github.com/vors/ZLocation) remembers directories you often navigate and rank them. After some time it will allow you to jump around with few letters. You can provide more letters (or regex) to narrow the search.

```powershell
# Install
Install-Module ZLocation -Scope CurrentUser

# Get module up and running
Import-Module ZLocation

# Navigate to some directories to make ZL learn
cd C:/Windows
cd C:/Users/

# Switch between using z
z win
z u

# List all in history
z

# Return to previous location
z -
```

> 💡 In order to make ZLocation work every time you start PowerShell, we need to add it to profile. It's decribed in next section.


### 2.8. PSFzf

[PSFzf](https://github.com/kelleyma49/PSFzf) wraps fzf (we installed it through scoop) and allows better usage in PS with few handy utilities. There are some nice helper functions (with aliases) that make other tasks that use fzf easier. We'll enable those in next section.

```powershell
# Install from PowerShell Gallery
Install-Module PSFzf

# Use it to switch directories
Get-ChildItem . -Attributes Directory | Invoke-Fzf | Set-Location

# Open VS code with selected file
Get-ChildItem . -Recurse -Attributes !Directory | Invoke-Fzf | % { code $_ }

# Use fd to get desired input to fzf and start VSCode with selected file
fd -e md | Invoke-Fzf | % { code $_ }
```

## 3. PowerShell profile setup

Now we need to do some additional setup to make things work for every PS instance. We'll also create some aliases from PSFzf module.

> 💡 You can call `Enable-PsFzfAliases` to set all aliases, but I prefer to manually set only ones I use.


Profiles are just scripts that run when shell is created. There are several of them but I would suggest using `$profile.CurrentUserAllHosts` so you have identical experience in all hosts. You can also use `$profile` which will use `CurrentUserCurrentHost`

Find more information on here: [https://devblogs.microsoft.com/scripting/understanding-the-six-powershell-profiles/](https://devblogs.microsoft.com/scripting/understanding-the-six-powershell-profiles/)

```powershell
# Check if we have PowerShell profile created
Test-Path $profile.CurrentUserAllHosts 

# This will create file if it doesn't exist
echo $null >> $profile.CurrentUserAllHosts 

# Open our profile file in VSCode (or use preferred editor)
code $profile.CurrentUserAllHosts 
```

You will have editor open with PowerShell profile file ready to be set up.

If you have your profile already set up, you can add this to it. I've added comments to describe what each line does.

Paste following in the opened editor:

```powershell
# To make ZLocation module work in every PowerShell instance.
Import-Module ZLocation

# PSFzf has undocumented option to use fd executable for
# file and directory searching. This enables that option.
Set-PsFzfOption -EnableFd:$true

# Custom function to SetLocation, because PSFzf uses
# Get-ChildItem which doesn't use fd and doesn't use
# ignore files. Invoke-FuzzySetLocation is defined here
# https://github.com/kelleyma49/PSFzf/blob/b97263a30addd9a2c84a8603382c92e4e6de0eeb/PSFzf.Functions.ps1#L142
# 
# This implementation is for setting FileSystem location
# and implementation uses parts of
# https://github.com/kelleyma49/PSFzf/blob/b97263a30addd9a2c84a8603382c92e4e6de0eeb/PSFzf.Base.ps1#L20
# https://github.com/kelleyma49/PSFzf/blob/b97263a30addd9a2c84a8603382c92e4e6de0eeb/PSFzf.Base.ps1#L35
function Invoke-FuzzySetLocation2() {
  param($Directory = $null)

  if ($null -eq $Directory) {
    $Directory = $PWD.Path 
  }

  $result = $null

  try {

    # Color output from fd to fzf if running in Windows Terminal
    $script:RunningInWindowsTerminal = [bool]($env:WT_Session)
    if ($script:RunningInWindowsTerminal) {
      $script:DefaultFileSystemFdCmd = "fd.exe --color always . {0}"	
    }
    else {
      $script:DefaultFileSystemFdCmd = "fd.exe . {0}"	
    }

    # Wrap $Directory in quotes if there is space (to be passed in fd)
    if ($Directory.Contains(' ')) {
      $strDir = """$Directory""" 
    }
    else {
      $strDir = $Directory
    }

    # Call fd to get directory list and pass to fzf
    Invoke-Expression (($script:DefaultFileSystemFdCmd -f '--type directory {0} --max-depth 1') -f $strDir) | Invoke-Fzf | ForEach-Object { $result = $_ }
  }
  catch {
        
  }

  if ($null -ne $result) {
    Set-Location $result
  } 
}

# Show tips about newly added commands
function Get-Tips {

  $tips = @(
    [pscustomobject]@{
      Command     = 'fcd'
      Description = 'navigate to subdirectory'
      
    },
    [pscustomobject]@{
      Command     = 'ALT+C'
      Description = 'navigate to deep subdirectory'
      
    },
    [pscustomobject]@{
      Command     = 'z'
      Description = 'ZLocation'
      
    },
    [pscustomobject]@{
      Command     = 'fz'
      Description = 'ZLocation through fzf'
      
    },
    [pscustomobject]@{
      Command     = 'fe'
      Description = 'fuzzy edit file'
      
    },
    [pscustomobject]@{
      Command     = 'fh'
      Description = 'fuzzy invoke command from history'
      
    },
    [pscustomobject]@{
      Command     = 'fkill'
      Description = 'fuzzy stop process'
      
    },
    [pscustomobject]@{
      Command     = 'fd'
      Description = 'find https://github.com/sharkdp/fd#how-to-use'
      
    },
    [pscustomobject]@{
      Command     = 'rg'
      Description = 'find in files https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md'
      
    }
  )

  Write-Output $tips | Format-Table
}

# Define aliases to call fuzzy methods from PSFzf
New-Alias -Scope Global -Name fcd -Value Invoke-FuzzySetLocation2 -ErrorAction Ignore
New-Alias -Scope Global -Name fe -Value Invoke-FuzzyEdit -ErrorAction Ignore
New-Alias -Scope Global -Name fh -Value Invoke-FuzzyHistory -ErrorAction Ignore
New-Alias -Scope Global -Name fkill -Value Invoke-FuzzyKillProcess -ErrorAction Ignore
New-Alias -Scope Global -Name fz -Value Invoke-FuzzyZLocation -ErrorAction Ignore
```

## 4. Usage with fzf

Real power of fzf comes when its result is piped to another operation. It can be used to open file or change directory (if input to fzf is directory/file list), kill process, navigate through command history, etc...

> 💡 There are more commands in PSFzf than ones covered here. They can be also used as a base for custom commands.


```powershell
# Call fe to get list of files to edit in current directory (or pass dir as param)
# After selecting file it will open VS Code (or set $env:EDITOR variable to executable)
fe
fe ./services

# List recent commands executed and search through them
fh
fh curl

# Find process to kill
fkill

# CHANGING DIRECTORY

# List one depth folders and change to it (respects ignore files)
fcd

# Get from recently navigated locations
z
z proj

# Using "z" by itself works, but if you need more control try
# which will list all frequent locations and give you fzf to change into
fz

# <Alt+C> - similar to fcd but lists all subdirectories

```

> 💡 Invoke `tips` to get quick refresher about newly added features. This function is defined in profile.


```powershell
PS C:\> tips

Command Description
------- -----------
fcd     navigate to subdirectory
ALT+C   navigate to deep subdirectory
z       ZLocation
fz      ZLocation through fzf
fe      fuzzy edit file
fh      fuzzy invoke command from history
fkill   fuzzy stop process
fd      find https://github.com/sharkdp/fd#how-to-use
rg      find in files https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md

```

## 5. Conclusion

Operations like navigating directories, operation history and editing files fall under basic commands you will do in terminal. With few tools and aliases added to PowerShell we got basic experience much enjoyable and flexible. Suggest more tools in the comments.