This marks the second installment in a series about useful CLI utilities. In this article, I'll delve into additional command-line tools that can enhance your productivity in the terminal. While the first article covered text and file processing tools, as well as process management, this one will focus on topics like searching within files and directories, along with project management.
File and directory
To effectively manage files and directories, it's beneficial to utilize a well-designed CLI tool that presents information in a clear and easily understandable format.
exa
Exa is the ls
command replacement.
It displays a list of files and directories, akin to the ls
command, but with added features such as colorized outputs,
Git integration, icons, support for a tree view, and more.
$ exa --git -lh --octal-permissions --color-scale
Octal Permissions Size User Date Modified Git Name
0755 drwxr-xr-x - mort 29 Oct 20:33 -- doc
0644 .rw-r--r-- 5.5k mort 18 Nov 13:04 -- flake.lock
0644 .rw-r--r-- 1.6k mort 29 May 07:57 -- flake.nix
0755 drwxr-xr-x - mort 20 Apr 14:44 -M home-manager
0644 .rw-r--r-- 14k mort 23 Apr 09:11 -- LICENSE
0755 drwxr-xr-x - mort 22 Apr 10:14 -- nixos
0644 .rw-r--r-- 2.3k mort 17 Nov 12:31 -- README.md
0755 drwxr-xr-x - mort 6 Jul 23:48 -- static
To simplify usage and avoid the need to remember all the flags, you can create an alias, similar to what I've done:
alias l="exa --git -lh --octal-permissions --color-scale --icons"
z / zoxide
If you're seeking a convenient way to navigate to a directory without specifying the entire path, consider using z
or zoxide
.
There have been many instances where I needed to swiftly jump into a directory with a distinctive name, and these solutions proved helpful.
While both z
and zoxide
can address the primary concern, if you're seeking specific features,
you'll need to examine each project individually for more information.
I personally use zoxide
, and in this example, I was searching for my "dotfiles" directory. I simply mentioned "dot" and here is the output:
$ z dot
mort/.../dotfiles $
Search & Lookup
Enhancing productivity in the terminal involves finding the best matches in files and directories. In this section, we'll discuss some of the ones I find most useful.
fzf
You might be familiar with it, fzf is a fuzzy finder designed to assist you in swiftly and interactively locating your desired file or directory path.
$ fzf
> go < 3/4
> go.mod
go.sum
main.go
When I'm engaged in a project, I often need to swiftly locate files, read snippets from them, and if it's the correct file, open it in Neovim.
Fzf serves as the ideal solution. It recursively indexes your directory, allowing you to search for a file while simultaneously previewing its content.
To enhance this process, you can utilize fzf's --preview
parameter and pair it with the bat
command.
$ fzf --preview 'bat {} --style=numbers --color=always'
> main < 1/4
> main.go | 1 package main 1/73
| 2 import "fmt"
| 3
| 4 func main() {
So, as you see, the bat
command proves to be quite useful when working with fzf
.
Additionally, you have the option to modify the default command behind the fzf command.
Personally, I opt for fd
due to its speed and efficiency:
export FZF_DEFAULT_COMMAND='fd --type f'
I've also configured f
and o
aliases in my zsh_aliases file
for swiftly accomplishing what I need:
alias f="fzf --preview 'bat {} --style=numbers --color=always'"
alias o="nvim `f || echo '-c :quitall'`"
You can leverage fzf
to perform text searches, such as finding the PID of a process:
$ procs | fzf
mcfly
For those accustomed to using ctrl-r
to search their terminal history, consider using mcfly
for a more convenient and enhanced experience.
Personally, I opt for fzf
to search my history.
ripgrep-all
Many individuals utilize ripgrep
for recursive searches, and to extend the search capabilities to include PDFs, Ebooks, archived, compressed,
and other file types, you can make use of the ripgrep-all
package.
$ rg main
main.go
1:package main
59:func main() {
bat-extras
bat-extras
encompasses various commands related to bat
.
One notable example is batgrep
, which combines ripgrep
and the bat
command to enhance the output for a better experience:
$ batgrep -i main
--------------------------------------------------------------------------------------------------------------------
File: main.go
1 package main
2
3 import (
...-----------------------------------------------------------------8<---------------------------------------------
57 }
58
59 func main() {
60 router := gin.New()
61
--------------------------------------------------------------------------------------------------------------------
Once installed, you can utilize its commands, such as batman
and batdiff
.
ast-grep
This is one of my favorite tools that facilitates searching in your source codes for more precise results based on
the Abstract Syntax Tree (AST).
For example, consider a scenario where you have a collection of code containing the word "func" (indicating a Golang function definition)
somewhere in your README file. If you were to use the grep
or ripgrep
commands, they might return instances of the "func" word in the README file,
whereas you're specifically searching for functions in your source code.
In this instance, I'm searching for any type of function that takes no parameters:
$ ast-grep run -p 'func $A()'
./main.go
27|func main() {
28| a := User{}
29|
30| tag := reflect.TypeOf(a).Field(0).Tag
31| fmt.Println(tag)
32|
33|}
Now, I'm searching for functions with only one parameter:
$ ast-grep -p 'func $A($B)'
./main.go
21|func EmailParser(email string) string {
22| fmt.Println("Not implemented")
23| return ""
24|}
As the result indicates, ast-grep
is a perfect choiece for these use cases.
Projects
Now, let's delve into project management in the terminal, which requires a combination of several commands. If you believe that simply using "mkdir" or "git clone" will suffice, I would advise against it, as it can become chaotic when dealing with numerous projects, making maintenance a challenging task.
ghq
The ghq
tool aids in project management by simplifying the workspace path introduction.
When you need to clone a project, you can provide the URL to ghq
, and it automatically clones the project to the appropriate path.
ghq
allows you to view a list of projects, create a new repository, and can be seamlessly combined with other tools we've discussed to swiftly
search and navigate to your desired project.
Let's clone a project:
$ ghq get https://github.com/neovim/neovim.git
clone https://github.com/neovim/neovim.git -> /home/mort/Workspaces/github.com/neovim/neovim
git clone --recursive https://github.com/neovim/neovim.git /home/mort/Workspaces/github.com/neovim/neovim
Cloning into '/home/mort/Workspaces/github.com/neovim/neovim'...
remote: Enumerating objects: 224216, done.
remote: Counting objects: 100% (43840/43840), done.
remote: Compressing objects: 100% (728/728), done.
remote: Total 224216 (delta 43285), reused 43125 (delta 43112), pack-reused 180376
Receiving objects: 100% (224216/224216), 179.78 MiB | 6.65 MiB/s, done.
Resolving deltas: 100% (179868/179868), done.
As the output indicates, it stores the project in the /home/mort/Workspaces/github.com/neovim/neovim
path without me explicitly specifying it.
Now, let's see the list of projects:
$ ghq list
github.com/mortymacs/abcmeta
github.com/mortymacs/dotfiles
github.com/neovim/neovim
github.com/mortymacs/nvim_context_vt
We can employ fzf
to search for a project and cd to its directory:
$ cd $GHQ_ROOT/$(ghq list | fzf -e)
> vim < 2/4
> github.com/neovim/neovim
github.com/mortymacs/nvim_context_vt
mort/.../neovim $ pwd
/home/mort/Workspaces/github.com/neovim/neovim
GHQ_ROOT
is a crucial variable that theghq
command is attentive to. STherefore, it's essential to set it to your workspace path. What I've done wasexport GHQ_ROOT="$HOME/Workspaces"
.
Conclusion
In this article, we delved into displaying files and directories with enhanced, human-readable information through colorized output.
We discussed how to locate a file or directory using fzf, and also explored a method for finding a function in our project with context,
avoiding unrelated results.
Finally, we concluded with a project management approach, combining various commands with the ghq
tool.