There are many tools that I use to improve my terminal experience, these are the top five that I use every single day to make developing code easier, quicker, and more enjoyable.

ack

Starting with the most well known first, ack is like grep but optimized for programmers, it is both faster and simpler than grep, and has better results.

ack has several quality of life improvements by default: recursive by default (but ignores VCS folders like .git), better output display including highlighting, and it uses PCRE for pattern matching.

Just compare the default output for grep -R and ack (respectively):

Search for “Interface” in the “src” directory using grep: grep -R Interface src
The same search using ack: ack Interface src

Sure, the output for ack takes up far more space, but it groups matches by filename (highlighted), adds line numbers, and highlights the matching text.

Additionally, you can easily include specific file types using the -t or --type= flags, e.g. to only search PHP file types which includes .php .phpt .php3 .php4 .php5 .phtml and files with a PHP shebang (e.g. #!/usr/bin/env php) on the first line you can use -t php or --type=php. You can also use the type name itself as a flag instead e.g. --php.

You can easily expand these types by adding a .ackrc file to your HOME directory and with a few lines either add additional file matches, or add a new file type entirely. For example, you can use --type-add to add composer.json (specifically) as a filename to search when searching through php file:

--type-add=php:is:composer.json

You can create a new file type for .env files called env:

--type-set=env:ext:env
--type-add=env:match:/^.env.*$/

Or to create a new file type for blade templates:

--type-set=blade:match:/.blade.php$/

With these in place a search for ack --php dshafik/bag will return results from composer.json, while a search for ack --blade someVariables will only match results in *.blade.php files, and ack --env APP will match all lines containing APP in your .env* files.

delta

delta describes itself as “a syntax-highlighting pager for git, diff, grep, and blame output”, but I find that to be somewhat ambiguous: it is a syntax-highlighting pager for git diff, git grep, and git blame output. It will actually be used by all git operations that output diffs, not just git diff.

Integration with git is easy, just add the following to the .gitconfig file in your HOME directory:

[core]
pager = delta

[interactive]
diffFilter = delta --color-only

[delta]
navigate = true # use n and N to move between diff sections

# delta detects terminal colors automatically; set one of these to disable auto-detection
# dark = true
# light = true

[merge]
conflictstyle = diff3

[diff]
colorMoved = default

Personally, I also like to add line-numbers = true to the [delta] configuration to enable line numbers.

Once you’ve completed this, git will start to output much easier to read diffs:

delta output with git diff

As you can see here, it calls out the filename at the top, and is smart enough to show the relevant code structure that contains the change and the line on which the diff context starts, in this case the namespace, starting from line 7, but it could also be a class or a function or method. It also features per-character diff highlighting, on top of the standard line highlighting — in this case you can see that I added a new value to the Attribute options bit mask.

httpie

The next tool has been in my toolkit for a long time, httpie, a python tool for making HTTP requests. It is usually referred to as “curl for humans”. Once installed, httpie is available as the http command, which then folllows a syntax similar to the HTTP protocol itself:

http <METHOD> <URL> <HEADER>:<HEADER VALUE> <HEADER2>:<HEADER2-VALUE>

for example, this will display the response from bagvalueobjects.com:

http GET https://bagvalueobjects.com

By default, it will show the response headers and the response body, but this is easy to change using the -p flag. The following will show only the request and response headers:

http -pHh GET https://bagvalueobjects.com

The value for -p are H (request headers), h (response headers), B (request body), b (response body).

Request headers and body? Well, yes, you can send POST requests just as easily:

http -pHhBb POST http://httpbin.org/post foo=bar

You can see that we have a new argument at the end foo=bar, this will send a JSON body with the value {"foo":"bar"}. You can add as many properties as you like to the end of the command. You can also pass the -f or --form argument and it will send the key-value pairs as multipart/form-data instead.

If you need to send complex JSON, use := instead, like so:

http -pHhBb POST http://httpbin.org/post foo:='["bar", "baz", "bat"]'

This will send the body {"foo":["bar", "baz", "bat"]}.

There are tons of other options including being able to use files as the post body, send files, support for sessions, authentication and more.

gron

Many folks are familiar with jq, a great command line utility for working with JSON, if you’re not familiar with it, I highly recommend that you check it out.

Sometimes however, jq is just too complicated, or the result isn’t exactly usable. Sometimes, you just want to be able ack (née grep) some JSON for a specific value. gron will break down JSON into valid Javascript assignments, for example, the simple body from our httpie example above might looks like this:

json = {};
json.foo = [];
json.foo[0] = "bar";
json.foo[1] = "baz";
json.foo[2] = "bat";

As you can see, this creates a variable json, and assigns an empty object to it, it them sets the foo key to an empty array, and then sets each of the numeric keys to it’s specific value.

Because it’s broken down to one assignment per line, you can now easily and reliable pipe it through ack or grep and find whatever you need.

A more practical example: list all the require-dev requirements in a composer.json files:

gron composer.json | ack require-dev

This will output the following:

json["require-dev"] = {};
json["require-dev"]["captainhook/captainhook-phar"] = "^5.23";
json["require-dev"]["captainhook/hook-installer"] = "^1.0";
json["require-dev"]["fakerphp/faker"] = "^1.23";
json["require-dev"]["infection/infection"] = "^0.28.1";
json["require-dev"]["larastan/larastan"] = "^2.0";
json["require-dev"]["laravel/pint"] = "^1.15";
json["require-dev"]["orchestra/testbench"] = "^8.0|^9.0";
json["require-dev"]["phpunit/phpunit"] = "^10.5|^11";
json["require-dev"]["ramsey/conventional-commits"] = "dev-allow-sf-7";
json["require-dev"]["roave/security-advisories"] = "dev-latest";
json["require-dev"]["symfony/var-dumper"] = "*";

We can then turn this back into JSON, and feed it into jq to get the values using gron -u or gron --ungron:

gron composer.json | ack require-dev | gron -u |  jq '."require-dev" | keys[]'

This will output:

"captainhook/captainhook-phar"
"captainhook/hook-installer"
"fakerphp/faker"
"infection/infection"
"larastan/larastan"
"laravel/pint"
"orchestra/testbench"
"phpunit/phpunit"
"ramsey/conventional-commits"
"roave/security-advisories"
"symfony/var-dumper"

Alternatively, let’s use gron to remove any custom scripts from our composer.json. We can use this by again, piping the output through ack, but this time using the -v argument to exclude matches, and then passing it back to gron -u:

gron composer.json | ack -v json.scripts | gron -u

The resulting JSON will be identical to the original, but with the entire scripts key missing.

bat

The last tool I want to cover is a new addition to my local environment, and that is bat, a replacement for cat with syntax highlighting.

bat has a few advantages over cat besides syntax highlighting by default: the output is paged, includes the file name, and line numbers:

syntax highlighted code using bat

While bat doesn’t provide as much utility as most of the other tools on this list, it’s simplicity makes it perfect as a drop-in replacement for cat, allowing you to just add alias cat=bat to your terminal configuration and forget about it.

Next Steps

If you want to try any of these tools for yourself, they are all available using brew, which is available on all platforms.

This list was aimed specifically at improving your development workflows, however, I have also made many changes to my terminal environment as a whole which also aids me in getting my work done faster, and easier. If you want to hear more, let me know in the comments below!

Update: I wrote a post on my terminal environment which you can read here: