Spice up PowerShell with a file finder

Published 8/17/2024 12:17:33 PM
Filed under Developers life

It's vacation time, so I allowed myself to mess around with my PowerShell configuration. I built a file finder in PowerShell with a few open source tools. I think it's awesome, but I'm also curious what you think.

This post covers the process for building a file finder from start to finish including a nice preview of a selected file. Let's get started with a short introduction to the PowerShell profile script and how to edit it.

Before we start, how to edit your profile script

It's important to understand that all code in this post goes into your PowerShell profile script. You can edit your profile script using the following command:

code $profile

After editing, you can save the profile script and reload the terminal to activate the new functions. Alternatively, you can use the following command to reload the profile script:

. $profile

I recommend keeping the profile script open in VS Code or another editor of your choice. Let's move on to building the actual file finder.

Building the file finder

To create a functional file finder, we'll need a function to select a file, and a function to handle the file selection. We'll start by creating the file selection logic.

Locating files with fzf

The basis for the file finder is the open source tool fzf. It's a fuzzy finder that can quickly filter through a list of files and/or directories. I combined it with fd, a tool that can quickly list out files and directories with specific filters.

Before we can build a basic file finder, we need to install two tools:

winget install junegunn.fzf
winget install sharkdp.fd

The basis of the file finder is the Find-File function which you can find below:

function Find-File {
    $file = fd --type file --follow --exclude .git | fzf 
    return $file
}

Add this function to the profile script and save it.

You can use the function from the terminal when you reload your profile script. When you enter Find-File in your terminal it will open up the fzf window and allow you to find a specific file. When you press ENTER, you'll get the path to the file.

Now this function doesn't do much on its own, but it's an important first step towards building the file finder. The next step is to do something useful with the file. For example, edit it in VS Code.

Handling file selection

When you select a file in the Find-File function you'll need to handle that selection in some way. In this step, we'll add another function called Invoke-FileAction that we'll call to select what to do with the chosen file.

Add the following function to your profile script:

function Invoke-FileAction ($Path) {
    $commands = @{
    "Edit file in Visual Studio Code" = { code $Path }
    "Delete file"                     = { Remove-Item -Recurse -Force $Path }
  }

    $selected_command = $commands.Keys | fzf --prompt "Select action >"
    &$commands[$selected_command]
}

The Invoke-FileAction function accepts a path to a file. It runs the fzf command with a list of commands you can choose from. When you choose one of the commands, we'll execute the command on the provided $Path.

You can expand the file management options by adding new key-value-pairs to the $commands dictionary.

Make sure to save and reload your profile script. You can test the function by running Invoke-FileAction -Path <path-to-a-file> in your terminal.

Let's combine the file selection and file management functions into a fully functional file finder.

Combining file selection and selection handling

We need one more function to build the file finder, Invoke-FileFinder, which will combine the two functions we wrote in the previous sections. You can find the content of the function below:

function Invoke-FileFinder() {
    $file = Find-File

    # Make sure we selected a file at all.
    if (!$file) {
        return
    }

    # Make sure we have a valid file path.
    if(Test-Path $file) {
        Invoke-FileAction -Path $file
    }
}

Nobody is going to write Invoke-FileFinder on the terminal, but I've got a fix for that. We can bind a keyboard shortcut to the function using the following code:

Set-PSReadLineKeyHandler -chord "ctrl+f" -scriptblock {
  [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine()
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert("Invoke-FileFinder")
  [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
}

The function Set-PSReadLineKeyHandler binds a key combination to a script block or built-in function. We use the -ScriptBlock argument to specify a set of commands to execute:

  1. First, we'll clear the current command that's next to the prompt.
  2. Next, we'll enter the command Invoke-FileFinder.
  3. Finally, we'll accept the input.

Add the code to your profile script and reload the terminal to bind the Ctrl+F key combination to the file finder.

You may be wondering why we're executing the file finder in this specific way. Wouldn't it be nicer to just call Invoke-FileFinder.

While testing, I found that my preview command wouldn't work if I invoked the file finder directly from the keybinding. I'm unsure why it doesn't work, but I found that entering the command through the PSConsoleReadline object works.

Now that you have a working file finder, let's make it even more useful with file previews.

Adding file previews to the file finder

Most people will find it useful to have some kind of preview of the file they're selecting in the file finder. So let's explore adding that next.

For the file preview to work, I'd like to use bat. This is an alternative to the cat command with a lot more options. It can list file contents with syntax highlighting, scrolling, line numbers, and even changes you made to the file.

Execute the following command to install bat on Windows 11:

winget install sharkdp.bat

Next, modify the Find-File function in your profile script to look like this:

function Find-File {
  $file_path = fd --type file --follow --exclude .git | 
  fzf --ansi --preview 'bat --color=always {} --style=numbers,changes'

  return $file_path
}

We're still using fd to list the files in the current directory and below. We modified the fzf command to include two extra options:

  1. First, we added --ansi to enable colored output for the preview window.
  2. Finally, we added the --preview option to set a command for previewing file content in the preview window.

The preview is generated using bat. We force the command to always use colored output, so we get nicely formatted output in the preview window.

Make sure to save your profile script and reload it. When you press Ctrl+F in your terminal you should get a window that looks like this:

File finder with preview in Powershell
File finder with preview in Powershell

Conclusion

In this post you learned how to build a file finder in PowerShell with the help of fd and fzf. We also covered how to bind a keyboard shortcut to the file finder. Finally, we covered how to use bat to generate a preview for the selected file in the file finder.

Let me know in the comments if you find this PowerShell trick useful!