Speaking UNIX

Go fish!

The Friendly Interactive Shell (fish) is ideal for UNIX novices and experts alike

Content series:

This content is part # of # in the series: Speaking UNIX

Stay tuned for additional content in this series.

This content is part of the series:Speaking UNIX

Stay tuned for additional content in this series.

English is a perplexing language. For example, consider the words moon and good. To the uninitiated, the words should rhyme, but the former is pronounced /mun/ (according to the International Pronunciation Alphabet), while the latter is spoken /good/. Seemingly, the only rule in English is exception.

UNIX shells are equally perplexing. For instance, in the Bourne shell (and most common UNIX shells), the phrases '$var', "$var", and `$var` look alike but yield substantially different results. (Each CLI in the shell examples presented in this article are prefaced with the name of the active shell and the command number.)

bash-1) # Demonstrate the differences between single-, double-, and back quotes
bash-2) var=ls
bash-3) echo '$var'
bash-4) echo "$var" 
bash-5) echo `$var`
Rakefile app bin components config db
    doc lib log patches public script src
    test tmp vendor

In the sequence above, the variable var is set to the two-letter string ls. In the initial echo command, the apostrophes prevent interpretation of the variable, instead yielding a verbatim copy of the quoted text, the four-letter string $var. Next, in command 4, the double quotation marks do interpret the variable, so the result is the string ls. Finally, the back quotes both interpret the variable and run the intermediate result as a subshell. Thus, `$var` yields the intermediate string ls, which runs as a shell command to produce the contents of the local directory.

Certainly, all three operators—the single, double, and back quotation marks—serve a valid purpose, but like exceptions in English, memorizing and mastering the variations can be maddening. Here's more proof: What's the difference between the phrase $var and "$var"? (Hint: Assume that $var contains whitespace.)

bash-1) # Create three files and try to remove two
bash-2) touch three two one
bash-3) var="one two"
bash-4) rm "$var"
rm: one two: No such file or directory
bash-5) rm $var
bash-6) ls

If a variable contains whitespace, the double quotation marks keep the variable expansion intact as a single argument. Otherwise, any whitespace in the variable is interpreted as an argument delimiter.

Yep. Shell syntax can be maddening. And that's unfortunate, because it makes the CLI—one of the most powerful features of UNIX—more difficult to learn. Indeed, inconsistencies like those described above fluster hardened veterans, too.

Thankfully, fish—the Friendly Interactive Shell—swims upstream against the tide of obfuscation, providing streamlined syntax and a much-improved user experience. Like other shells, fish provides redirection, shortcuts, globbing (that is, expansion of wildcards), subshells, tab completion, and variables. Unlike alternatives, however, fish also provides color-coded CLIs, an extensive command-line editor, and rich documentation.

Additionally (and intelligently), fish provides only one way to do anything. If a UNIX utility achieves a particular task, fish does not repeat the feature as a built-in command. For example, fish uses the system-wide application /bin/kill to terminate processes. (By comparison, the Bourne shell implements its own version of kill as a built-in application. You can type /bin/kill at the Bourne shell prompt to use the application, instead.) Whenever possible, fish prefers simplicity over flexibility, making it far more predictable to use.

Here, let's install fish, reel it in, and try just some of its many features.

Catching fish

Fish is an open source project created by Axel Liljencrantz and licensed under the GNU General Public License, version 2. As of this writing, the latest version of fish is 1.23.0, released 13 January 2008.

If you use UNIX or a UNIX-like system, such as Linux® or Mac OS X, fish should build from source code readily and easily on your system. Here are the steps, as shown in Listing 1:

  1. Download the most recent source tarball of the program.
  2. Unpack it.
  3. Change to the source directory.
  4. Configure the build.
  5. Run make.
Listing 1. Build fish from source
bash-1) wget
bash-2) tar xzvf fish-1.23.0.tar.gz
bash-3) cd fish-1.23.0
bash-4) ./configure --without-xsel
checking if autoconf needs to be run... no
checking if autoheader needs to be run... no
checking for /usr/pkg/include include directory... no
bash-5) make
gcc -c -o function.o function.c
bash-6) sudo make install
To use fish as your login shell:
* add the line '/usr/bin/fish' to the file '/etc/shells'.
* use the command 'chsh -s /usr/bin/fish'.

If you're using a UNIX-like system, the additional flags to configure aren't strictly needed. However, to minimize dependencies and to keep fish in the same directory structure as common shells, you can add --without-xsel and --prefix=/usr, respectively. (If you use Mac OS X version 10.4 Leopard, also add the parameter LDFLAGS=-liconv. If you omit the latter option on Mac OS X, the attendant fish utilities fail to build.)

Optionally, if you use a popular UNIX variant, you can likely find pre-built binaries ready to install on your distribution. For example, if you use Debian Linux, you can install fish in an instant with the command sudo apt-get install fish. Check the fish project home page for availability for your system.

School is in

Before diving in to more complex topics, let's look at how common shell tasks are accomplished in fish:

  • To redirect standard input and standard output, use the operators < and >, respectively. To redirect standard error, use the carat (^), as shown in Figure 1. To append standard error to a file, use ^^.
    Figure 1. Redirect standard error with the caret operator
    Redirect standard error with the caret operator
    Redirect standard error with the caret operator

    In command 3, the error messages produced by rm are captured in the file named errors. Command 4 shows the contents of the file. The fish shell has rich support for redirection, such as combining descriptors into one stream and closing descriptors.

    By the way, the colored and underlined text shown aren't editorial. The shell highlights text in the CLI as you type. In the lines, green indicates that the command name is valid; an invalid command name is colored red. The underline is a hint that the named file exists. (A section below covers shell feedback in more detail.)

  • To run a subshell, use the parentheses (()), as shown in Figure 2. Text enclosed within parentheses is interpreted as a list of commands and replaced by the result.
    Figure 2. Use parentheses to run a subshell
    Use parentheses to run a subshell
    Use parentheses to run a subshell
  • To create an alias, or shortcut, create a fish function.

    A function can contain one or more commands, and the special variable $argv automatically expands to the list of arguments passed on the original command line.

    You can list all defined functions with the command functions. To erase a function, use functions --erase name, as in functions --erase ll.

    You can also instantly save any function you write at the command line. When your code is complete, type funcsave name, such as funcsave ll. The function is immediately available to all your currently running shells and all future shells, as well. The command funced name interactively edits an existing function. The funced command has full syntax highlighting, tab completion, and automatic indenting; funcsave and funced make it easy to customize your shell.

  • To set a variable, type set variable namevalue. As with the functions built-in variable, type set --erase variable name to "unset," or erase, a variable. To retrieve the value stored in the variable, type a dollar sign ($) followed by the variable's name, as shown in Figure 3.
    Figure 3. Test for the existence of a variable
    Test for the existence of a variable
    Test for the existence of a variable

    Smartly, fish provides the --query option to test whether a variable is defined. If the variable is set, set --query returns a status code of 0, indicating that no error occurred; otherwise, it returns a 1. Statement 6 chains two commands with the or operator: The second command (echo) executes only if the first command fails.

So, how does fish handle the dreaded $var, '$var', "$var", and `$var`? True to form, it follows a few simple rules:

  • If a variable contains whitespace, the whitespace is always preserved, and the variable always evaluates to a single argument (see Figure 4.
    Figure 4. Fish keeps strings with embedded whitespace intact
    Fish keeps strings with embedded whitespace intact
    Fish keeps strings with embedded whitespace intact
  • If the double quotation mark is the outermost quote, all variables are expanded.
  • If the single quotation mark is the outermost quote, no variables are expanded.

Let's look at how these rules work in practice.

Command 1 creates four files, where the last file has whitespace in its name. Commands 3 and 4 delete the file named by the variable file. Commands 6 and 7 delete the two files named in the twofiles variable. Look closely at command 6: Because the value is not placed in quotation marks (either single or double), whitespace is not protected. Hence, command 7 expands the variable into two arguments and deletes both files. Commands 9 and 10 reiterate the scenario in commands 6 and 7.

Commands 11 and 12 demonstrate the whitespace rule. Even though the variable is not surrounded by double quotation marks in command 12, fish maintains the whitespace set in command 11. Very nice.

Commands 14 through 16 exhibit fish's nested quoting rules. Now, glance again at commands 11, 15, and 16. The shell uses color codes to display balanced quotation marks and reinforce proper syntax. Look, too, at commands 9 and 11. The latter command underlined the file name, indicating that it exists. The missing underline in command 9 is a big hint you did something wrong.

Friendly is fish's first name.

A great feature for landlubbers

Speaking of friendly features, fish's tab completion feature is another novelty that new UNIX users—and experts—find extremely useful. To see completion in action, type along in the example that follows. Click the Tab key at the end of each line.

If you're unsure of a command name, you can click Tab after typing a few letters to view a list of possible completions, as Figure 5 shows. (The list of completions on your system may differ from the list shown here. The list depends both on your PATH environment variable and the contents of your UNIX system.)

Figure 5. Click Tab to complete a command name
Press Tab to complete a command name
Press Tab to complete a command name

Notice the red text in the CLI. If fish doesn't recognize the name of a command, it is shown in red. Clicking Tab reveals all the application names—with a brief description—that begin with what you've you typed so far. You can also click Tab at an empty prompt to see all applications in your PATH.

If you'd like to know what options are available for a command, click Tab after a hyphen (-) or double hyphen (--), as shown in Figure 6.

Figure 6. You can also click Tab to complete an option
You can also press Tab to complete an option
You can also press Tab to complete an option

Here, fish tells you which options are available. The shell maintains a large index of common commands and options, and chances are, you can get the help you need. However, custom or more esoteric utilities may lack such data. You can read the fish documentation to learn more about writing your own completions.

You can also click Tab after typing a few letters of the option, as Figure 7 shows. The shell displays all the possible matches.

Figure 7. You can type part of an option, too
You can type part of an option, too
You can type part of an option, too

If you don't know what kind of operand a command manipulates, fish can help—in many cases, but not all. For example, if you type set (or vared, the fish variable editor), a space, and then press Tab, fish presents a list of available variables. The operand for set is an argument. Similarly, if you type type, a space, and then click Tab, fish displays a list of built-ins and functions that extend which utilities are available to you on the file system.

In general, all the built-ins and functions included with fish have context-sensitive operand completion. Try cd, for example, as shown in Figure 8.

Figure 8. Many commands are context sensitive and present suitable arguments
Many commands are context sensitive and present suitable arguments
Many commands are context sensitive and present suitable arguments

The cd function is a fish function and is aware that its operand is an existing directory. When you click Tab after typing cd, fish presents all the existing directories contained in every directory in your CDPATH.

Another smart completion is associated with ssh. Type ssh followed by a space, and then click Tab to see a list of known hostnames taken from your Secure Shell known hosts file (typically found in ~/.ssh/known_hosts):

fish-1) ssh  (Hostname)         (Hostname)

The fish shell also completes file names and directory names. Again, it highlights correct elements as you type path names.

One significant difference between fish and other shells is the lack of history shorthand, such as !, !!, and !$.

Casting for more fish

If you like fish and want to adopt it as your login shell, add the path to fish to the official list of shells, /etc/shells, and then run chsh:

bash-1) type fish
fish is /usr/bin/fish
bash-2) sudo vi /etc/shells
bash-3) cat /etc/shells
bash-4) chsh -s /usr/bin/fish
Changing shell for strike
Password: ********
bash-5) login strike
Password: ********
Last login: Wed Oct  8 15:02:21 on ttys000
Welcome to fish, the friendly interactive shell
Type help for instructions on how to use fish
fish-1) echo $SHELL


There is a lot to discover and like in fish. Dare I say it? "There is a lot of C in the fish."

You can tweak the colors in syntax highlighting. You can customize your startup by editing ~/.config/fish/ You can share variables across shell instances using universal variables and fishd. The shell also has a great history search feature, an interactive variable editor, and an interactive command-line editor.

Best of all, there is a tremendous amount of documentation all available from fish itself. If you need help, just type help at any command prompt.

The doctors are right: fish is good for you.

Downloadable resources

Related topics

Zone=AIX and UNIX
ArticleTitle=Speaking UNIX: Go fish!