Linux and Unix-like operating systems are lucky enough to have quite a few alternatives to almost every component of their operating environment. One of the components that server users interact with the most is the command line shell.
While most systems ship with the bash
shell, which stands for “Bourne again shell” after the creator of the original sh
shell that was its predecessor, there are other options that many users would benefit from checking out. You may have heard of the popular zsh
, which you can learn about here.
Another full-featured shell, which will be the subject of this guide, is the fish
shell. The fish
shell is a modern, attractive, and powerful command shell that can extend the capabilities of the usual bash
shell. In this guide, we’ll learn how to install, configure, and use this shell alternative.
We will be using these on an Ubuntu 12.04 VPS instance, but you should have an easy time adapting this to the distribution of your choice.
Fortunately for us, the default Ubuntu 12.04 repositories contain the fish
binary packages.
We can install it in the conventional way by updating our local package index and pulling the package onto our system:
sudo apt-get update
sudo apt-get install fish
That is all we need to do to get the new shell onto our system.
To start playing around, we’ll start a new fish
shell from within our current session. We’ll discuss later on how to change your default shell if you decide that fish
is for you:
fish
demouser@host ~>
You can see that your prompt changed. Instead of a “$” as a prompt for a normal user, you will see a “>” character.
Right away, we can begin to see some of the advantages of this shell by just going about our normal routines in the shell. These are mostly accomplished by included functions that we will look into later on.
For example, if you list the contents of a directory, you’ll notice that they are automatically appended with a character at the end that indicates the type of file:
ls
bin/ etc/ lib/ media/ proc/ sbin/ sys/ var/
boot/ home/ lib64/ mnt/ root/ selinux/ tmp/ vmlinuz@
dev/ initrd.img@ lost+found/ opt/ run/ srv/ usr/
This is the same output as ls -F
, which classifies contents by type.
If you type in a file path, whether relative or absolute, you will see that the fish
shell underlines directory paths to make it easier to interpret at a glance:
<pre> cd <span style=“text-decoration: underline;”>/home/demo</span> </pre>
If your terminal has the ability to display colored output, you’ll have noticed that your prompt is automatically colored as well. Furthermore, it is dynamically colored. If you type something that is not a valid command, it will show up as red.
This would be red:
ech
However, when you add the final “o”, turning it into a command, you’ll see it instantly turn green. This provides you with useful feedback that can make it easy to spot typos early.
You may also notice that the TAB completion is excellent:
cd / ## Hit the TAB key at this point
/bin/ (Directory) /media/ (Directory) /srv/ (Directory)
/boot/ (Directory) /mnt/ (Directory) /sys/ (Directory)
/dev/ (Directory) /opt/ (Directory) /tmp/ (Directory)
/etc/ (Directory) /proc/ (Directory) /usr/ (Directory)
/home/ (Directory) /run/ (Directory) /var/ (Directory)
/lib/ (Directory) /sbin/ (Directory)
/lib64/ (Directory) /selinux/ (Directory)
As you can see, fish
intelligently lists only the directories for the cd
command, since these are the only values that make sense.
If we were to use a more generic command, we’d see all listings (and their type) instead:
touch / ## Hit the TAB key at this point
/bin/ (Directory) /proc/ (Directory)
/boot/ (Directory) /root/ (Directory)
/dev/ (Directory) /run/ (Directory)
/etc/ (Directory) /sbin/ (Directory)
/home/ (Directory) /selinux/ (Directory)
/initrd.img (Symbolic link, 15MB) /srv/ (Directory)
/lib/ (Directory) /sys/ (Directory)
/lib64/ (Directory) /tmp/ (Directory)
/lost+found/ (Directory) /usr/ (Directory)
/media/ (Directory) /var/ (Directory)
/mnt/ (Directory) /vmlinuz (Symbolic link, 5.2MB)
/opt/ (Directory)
A similar feature that is helpful is the formatting of the man
command. If we want to see all of the fish
man pages, we can use tab completion:
man fish ## Hit the TAB key at this point
fish (1: the friendly interactive shell)
fish_indent (1: indenter and prettifier)
fish_pager (1: internal command used by fish)
fishd (1: universal variable daemon)
In a similar vein, you can get full-featured help using whatever terminal web browser you have using the built-in help system:
help
fish home | Main documentation page | Design document | Commands | FAQ |
License
Fish user documentation
1
Table of contents
• Fish user documentation
□ Table of contents
□ Introduction
□ Syntax overview
□ Help
□ Tab completion
On my machine, this opened the help system in the w3m
terminal web browser. You can follow any links like you would in a normal browser, and can quit by typing “q”. If you want to see the help for a specific command that fish knows about, just use it as an argument afterwards:
help cd
cd - change directory
Synopsis
cd [DIRECTORY]
Description Changes the current
directory. If DIRECTORY is supplied it will become the new directory. If
DIRECTORY is a relative path, the paths found in the CDPATH environment
variable array will be tried as prefixes for the specified path. If
CDPATH is not set, it is assumed to be '.'. If DIRECTORY is not
specified, $HOME will be the new directory.
Back to index.
Again, you can see what help commands are available by using TAB completion:
help ## Type a space to let fish know you are finished with the command, and then hit the TAB key at this point
alias (Help for the specified command)
and (Help for the specified command)
begin (Help for the specified command)
bg (Help for the specified command)
bind (Help for the specified command)
block (Help for the specified command)
break (Help for the specified command)
breakpoint (Help for the specified command)
. . .
Many people who have used bash
and even sh
for years will have grown accustomed to the way that these shells do things. While fish
does carry on much of the legacy of these shells, it modifies behavior where it can provide improvements.
One easy example of this is with redirection. Normal redirection and pipes work the same as with bash
:
However, one difference is the way that you redirect standard error. You do this with the carat character:
This provides an easy way to redirect one file descriptor to another. Recall that each file descriptor is usually associated with a number:
We can redirect one file descriptor to another by using the “&” character followed by the descriptor number.
For instance, we can redirect a command’s standard output into a file and then point its standard error to our standard output file as well by typing something like this:
ls /etc >ls_results.txt ^&1
All of the standard output is put into the ls_results.txt
file and then the standard error is set to the location that the standard output is being directed (the file above).
As for wildcards, fish
again uses most of the defaults from bash
. These are included:
The one additional wildcard that is extremely helpful is the recursive wildcard:
This can be used to easily add recursive functionality to commands. Even though ls
has a recursive option, we can do this with fish
. We could find all files that end in .conf
in our /etc
directory by typing:
ls /etc/**.conf
/etc/adduser.conf
/etc/apparmor/subdomain.conf
. . .
On my machine, the first two lines of output show this in action. One file is in the top directory that we were searching and the next is in a subdirectory.
We can create functions and aliases in fish
with an easy to use syntax.
The basic format is something like:
<pre> function <span class=“highlight”>function_name</span> <span class=“highlight”>function_content</span> end </pre>
If you wish to parse arguments within your function, you have them available all bundled together within the $argv
variable. They are stored as an array.
For instance, we can make a function like this that will print out all of our arguments:
function say_hello
echo hello $argv
end
We could call this with one or more arguments and it’ll pass them all to the echo
command:
say_hello John Doe
hello John Doe
If we want to access a specific variable, pull it out of the argument array by reference number (in fish
, arrays start at 1, not 0). We could modify our previous script to use only the 2nd argument:
function hello_sir
echo hello Mr. $argv[2]
end
We could then call this function and we’d get a different result:
hello_sir John Doe
hello Mr. Doe
We can see all of the defined functions by typing:
functions -a
You can delete one of your functions by typing:
<pre> functions -e <span class=“highlight”>function_name</span> </pre>
For aliases, the bash
shell has a specific command. In fish
, it uses the same function syntax.
The only thing to be aware of is that if the command supersedes or replaces the command it is referencing, you must add the command
builtin to tell the shell not to recursively call the function, but use the exterior command.
For instance, if we want the cat
command to include numbering by default, we might want to redefine the command to include that flag. Remember to pass the argument variable so that it can parse the filenames correctly:
function cat
command cat -n $argv
end
Now, when we call cat
, the output will be automatically numbered:
cat /etc/hosts
1 127.0.0.1 localhost fish fish
2
3 # The following lines are desirable for IPv6 capable hosts
4 ::1 ip6-localhost ip6-loopback
5 fe00::0 ip6-localnet
6 ff00::0 ip6-mcastprefix
7 ff02::1 ip6-allnodes
8 ff02::2 ip6-allrouters
If you are using a function to override the defaults of a command, you can also use the command
builtin to bypass any modifications you have made and get the original command.
command cat /etc/hosts
127.0.0.1 localhost fish fish
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
While bash
provides a very complex, but useful set of history functions, fish
pares these down and works on improving the basics to address usage issues.
You can move up chronologically in your history by using the UP
key. You can move in the reverse direction by using the DOWN
key. This is fairly standard.
If we wish to return to our prompt, we just hit the escape key.
We can also type in part of a previous command and then press the UP
key to search for the latest instances of that specific command.
Furthermore, we can use the ALT-UP
and ALT-DOWN
commands to recall the command line arguments only.
For instance, let’s say we listed the contents of a directory:
ls /etc
acpi/ groff/ ltrace.conf rmt*
adduser.conf group magic rpc
alternatives/ group- magic.mime rsyslog.conf
apm/ grub.d/ mailcap rsyslog.d/
apparmor/ gshadow mailcap.order screenrc
. . .
We realize that this is the directory we are looking for and we want to switch to it now. We can start by entering the new command:
cd # Don't press return yet
Now, we can insert in the arguments from the last command by hitting the ALT-UP
keys:
cd # Hit Alt-UP and get...
cd /etc
This is a very simple example, but you can see how this could potentially be incredibly useful, especially since you can scroll through previous command arguments.
Another kind of history that fish
provides is directory history. This is a great feature that allows you to basically move back through your cd
history to get to previous directories.
You can see your directory history by typing:
dirh
You can move backwards and forwards in your directory history by pressing ALT-LEFT
and ALT-RIGHT
when at an empty command prompt. This will allow you to easily cycle between directories.
If you find that you enjoy the fish
shell, you’ll probably want to include some customizations to shape your environment.
While you might be used to putting customizations in your ~/.bashrc
or ~/.bash_profile
files, these are not used for this shell.
To configure your preferences, you should create a file at ~/.config/fish/config.fish
. All fish
configuration files must end in .fish
. Usually, the ~/.config/fish/
path will be created when you use the shell for the first time.
If you would like to start with an example file, you can copy it from the fish
package directory:
cp /usr/share/fish/config.fish ~/.config/fish
You can then edit it like any other file:
nano ~/.config/fish/config.fish
When you get acquainted with the file, you should probably remove anything that you have not customized personally.
It is best not to add functions directly into this configuration file. Instead, you should create a directory called functions
within your fish
configuration directory:
mkdir ~/.config/fish/functions
Inside of this directory, create files for each of the functions you wish to make. As long as each file ends with .fish
, the shell will find them and incorporate them into its environment. Each function must be in its own file with nothing else.
For instance, we could create a file to make our hello_sir
function available in every session for our user, we could type this (before continuing, remember to unset the cat
alias we made earlier with set -e cat
if you haven’t done so already):
cat > ~/.config/fish/functions/hello_sir.fish
function hello_sir
echo hello Mr. $argv[2]
end
After typing end
, hit CTRL-D
to end the input. This will now be available every time the shell is loaded. If we wanted to also add our say_hello
function, we’d need a separate file.
If you need some inspiration, you can take a look at the default fish
functions:
cd /usr/share/fish/functions
ls
After you’ve gotten your shell configured to your liking, you may wish to use fish
as your default shell. To do this, you can use the chsh
command.
First, we need to know the path to the fish
shell:
which fish
/usr/bin/fish
Next, we can change our shell by typing:
chsh -s /usr/bin/fish
You will be asked for your password to confirm. Once this is complete, every time you login, you will be given a fish
prompt.
If you want to change back to your other shell, you can specify it by path in the same way:
chsh -s /bin/bash
By now, you should be relatively familiar with the basics of using the fish
shell. It is a good middle ground for many people, because it sticks to convention where it makes sense, but adds functionality where previous shells left room for improvement.
There are definitely more things to learn, but you should have a good foundation for further exploration. Remember to utilize the excellent help system that is available through the help
command.
<div class=“author”>By Justin Ellingwood</div>
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Also check Oh My Fish framework for a lot of nice plugins and prompt themes for Fish Shell: https://github.com/oh-my-fish/oh-my-fish
https://github.com/bpinto/oh-my-fish inspired by oh-my-zsh
Wow! This was… wow! Very cool
I just wonder if I will be able to leave behind the old bash habits