Let’s now see how I have configured my environment.
In fact, my config is
rather short; in config.fish I have the following:
set -x FZF_DEFAULT_OPTS "-m --bind='ctrl-o:execute(nvim {})+abort'"
set -x FZF_DEFAULT_COMMAND 'rg --files'
set -x EDITOR nvim
set -x KUBE_EDITOR nvim
set -ga fish_user_paths /usr/local/bin
My prompt, defined in fish_prompt.fish, looks as follows:
function fish_prompt
set -l retc red
test $status = 0; and set retc blue
set -q __fish_git_prompt_showupstream
or set -g __fish_git_prompt_showupstream autofunction _nim_prompt_wrapper
set retc $argv[1]
set field_name $argv[2]
set field_value $argv[3]
set_color normal
set_color $retc
echo -n '─'
set_color -o blue
echo -n '['
set_color normal
test -n $field_name
and echo -n $field_name:
set_color $retc
echo -n $field_value
set_color -o blue
echo -n ']'
end
set_color $retc
echo -n '┬─'
set_color -o blue
echo -n [
set_color normal
set_color c07933
echo -n (prompt_pwd)
set_color -o blue
echo -n ']'
# Virtual Environment
set -q VIRTUAL_ENV_DISABLE_PROMPT
or set -g VIRTUAL_ENV_DISABLE_PROMPT true
set -q VIRTUAL_ENV
and _nim_prompt_wrapper $retc V (basename "$VIRTUAL_ENV")
# git
set prompt_git (fish_git_prompt | string trim -c ' ()')
test -n "$prompt_git"
and _nim_prompt_wrapper $retc G $prompt_git
# New line
echo
# Background jobs
set_color normal
for job in (jobs)
set_color $retc
echo -n '│ 'set_color brown
echo $job
end
set_color blue
echo -n '╰─> '
set_color -o blue
echo -n '$ '
set_color normal
end
The preceding prompt definition yields the prompt shown in Figure 3-7;
note the difference between a directory that contains a Git repo and one that
does not, a built-in visual cue to speed up your flow. Also, notice the
current time on the righthand side.
Figure 3-7. Fish shell prompt
My abbreviations—think of these as alias replacements, as found in other
shells—look as follows:
$ abbr
abbr -a -U -- :q exit
abbr -a -U -- cat bat
abbr -a -U -- d 'git diff --color-moved'
abbr -a -U -- g git
abbr -a -U -- grep 'grep --color=auto'
abbr -a -U -- k kubectl
abbr -a -U -- l 'exa --long --all --git'
abbr -a -U -- ll 'ls -GAhltr'
abbr -a -U -- m make
abbr -a -U -- p 'git push'
abbr -a -U -- pu 'git pull'
abbr -a -U -- s 'git status'
abbr -a -U -- stat 'stat -x'
abbr -a -U -- vi nvim
abbr -a -U -- wget 'wget -c'To add a new abbreviation, use abbr --add. Abbreviations are handy for
simple commands that take no arguments. What if you have a more
complicated construct you want to shorten? Say you want to shorten a
sequence involving git that also takes an argument. Meet functions in Fish.
Let’s now take a look at an example function, which is defined in the file
named c.fish. We can use the functions command to list all defined
functions, the function command to create a new function, and in this case
the command function c to edit it as follows:
function c
git add --all
git commit -m "$argv"
end
With that we have reached the end of the Fish section, in which we walked
through a usage tutorial and configuration tips. Now let’s have a quick look
at other modern shells.
Z-shell
Z-shell, or zsh, is a Bourne-like shell with a powerful completion system
and rich theming support. With Oh My Zsh, you can pretty much configure
and use zsh in the way you’ve seen earlier on with fish while retaining
wide backward compatibility with bash.
zsh uses five startup files, as shown in the following example (note that if
$ZDOTDIR is not set, zsh uses $HOME instead):
$ZDOTDIR/.zshenv
$ZDOTDIR/.zprofile
$ZDOTDIR/.zshrc
$ZDOTDIR/.zlogin
$ZDOTDIR/.zlogout
Sourced on all invocations of the shell. It should contain commands to
set the search path, plus other important environment variables. But it
should not contain commands that produce output or assume the shell is
attached to a tty.Meant as an alternative to .zlogin for ksh fans (these two are not
intended to be used together); similar to .zlogin, except that it is sourced
before .zshrc.
Sourced in interactive shells. It should contain commands to set up
aliases, functions, options, key bindings, and so on.
Sourced in login shells. It should contain commands that should be
executed only in login shells. Note that .zlogin is not the place for alias
definitions, options, environment variable settings, and the like.
Sourced when login shells exit.
For more zsh plug-ins, see also the awesome-zsh-plugins repo on GitHub.
If you want to learn zsh, consider reading “An Introduction to the Z Shell”
by Paul Falstad and Bas de Bakker.
Other Modern Shells
In addition to fish and zsh, there are a number of other interesting—but
not necessarily always bash-compatible—shells available out there. When
you have a look at those, ask yourself what the focus of the respective shell
is (interactive usage vs. scripting) and how active the community around it
is.
Some examples of modern shells for Linux I came across and can
recommend you have a look at include the following:
Oil shell
Targets Python and JavaScript users. Put in other words, the focus is
less on interactive use but more on scripting.
murex
A POSIX shell that sports interesting features such as an integrated
testing framework, typed pipelines, and event-driven programming.
Nushell
An experimental new shell paradigm, featuring tabular output with a
powerful query language. Learn more via the detailed Nu Book.PowerShell
A cross-platform shell that started off as a fork of the Windows
PowerShell and offers a different set of semantics and interactions than
POSIX shells.
There are many more options out there. Keep looking and see what works
best for you. Try thinking beyond bash and optimize for your use case.
Which Shell Should I Use?
At this point in time, every modern shell—other than bash—seems like a
good choice, from a human-centric perspective. Smooth auto-complete,
easy config, and smart environments are no luxury in 2022, and given the
time you usually spend on the command line, you should try out different
shells and pick the one you like most. I personally use the Fish shell, but
many of my peers are super happy with the Z-shell.
You may have issues that make you hesitant to move away from bash, such
as the following:
You work in remote systems and/or cannot install your own shell.
You’ve stayed with bash due to compatibility and/or muscle memory.
It can be hard to get rid of certain habits.
Almost all instructions (implicitly) assume bash. For example, you’ll
see instructions like export FOO=BAR, which is bash specific.
It turns out that these issues are by and large not relevant to most users.
While you may have to temporarily use bash in a remote system, most of
the time you will be working in an environment that you control. There is a
learning curve, but the investment pays off in the long run.
With that, let’s focus on another way to boost your productivity in the
terminal: multiplexer.Terminal Multiplexer
We came across terminals at the beginning of this chapter, in “Terminals”.
Now let’s dive deeper into the topic of how to improve your terminal usage,
building on a concept that is both simple and powerful: multiplexing.
Think of it in this way: you usually work on different things that can be
grouped together. For example, you may work on an open source project,
author a blog post or docs, access a server remotely, interact with an HTTP
API to test things, and so forth. These tasks may each require one or more
terminal windows, and often you want or need to do potentially
interdependent tasks in two windows at the same time. For example:
You are using the watch command to periodically execute a directory
listing and at the same time edit a file.
You start a server process (a web server or application server) and
want to have it running in the foreground (see also “Job control”) to
keep an eye on the logs.
You want to edit a file using vi and at the same time use git to query
the status and commit changes.
You have a VM running in the public cloud and want to ssh into it
while having the possibility to manage files locally.
Think of all these examples as things that logically belong together and that
in terms of time duration can range from short term (a few minutes) to long
term (days and weeks). The grouping of those tasks is usually called a
session.
Now, there are a number of challenges if you want to achieve this grouping:
You need multiple windows, so one solution is to launch multiple
terminals or, if the UI supports it, multiple instances (tabs).
You would like to have all the windows and paths around, even if you
close the terminal or the remote side closes down.