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:
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:
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:
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:
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:
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
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:
/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
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 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:
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:
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
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-DOWN commands to recall the command line arguments only.
For instance, let’s say we listed the contents of a directory:
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
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:
You can move backwards and forwards in your directory history by pressing
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
~/.bash_profile files, these are not used for this shell.
To configure your preferences, you should create a file at
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:
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:
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 end
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
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
First, we need to know the path to the
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
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
<div class=“author”>By Justin Ellingwood</div>
Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.Sign up
Click below to sign up and get $100 of credit to try our products over 60 days!