Making Fish Shell Smile

When working in a shell, from time to time, I need to know if a command succeeded or failed. Sometimes, it’s easy:

$ make noise

make: *** No rule to make target `noise'.  Stop.

Sometimes, less so:

$ grep frog podcasts.json > podcasts-about-frogs.txt

Since, alas, I don’t have any podcasts about frogs, that command would fail silently. But that’s fixable!

$ grep frog podcasts.json > podcasts-about-frogs.txt

$ # Bash/Zsh
$ echo $?
1

$ # Fish
$ echo $status
1

A bit annoying, but functional. We can do better though. My current shell of choice is Fish. I have a few different things available on my prompt, including if the last command succeeded or failed:

><> ^_^ jp@neptune {git master} ~/Projects/blog
$ false


><> ;_; jp@neptune {git master} ~/Projects/blog
$

Things to note:

  • A cute ascii art fish ><>, just because
  • A happy (^_^) or sad (;_;) face depending on the last command
  • My current username (jp) and hostname (neptune)
  • If we’re in a git repo, the current branch
  • The current directory

Fish renders the prompt by calling the function fish_prompt, which can be found at ~/.config/fish/functions/fish_prompt.fish:

First, render the fish and capture the $status (so future commands in this function don’t confuse it):

function fish_prompt
    set last_status $status
    printf "\n><> "

Then, we’ll render either a smiley or sad face based on the last command:

    # Different status if the last command was successful
    if test $last_status -eq 0
        set_color green
        printf "^_^ "
    else
        set_color red
        printf ";_; "
    end

As an added bonus, the face is either green or red, which honestly I’m more likely to notice at a glance than the picture itself. It’s much easier (IMO) to implement colors in Fish than in Bash with command sequences.

Next up, the current machine:

    # Where am I (current user, machine, git, and path)
    set_color purple
    printf (whoami)
    set_color white
    printf "@"
    set_color yellow
    printf (hostname -s)

And then git status, but only if we’re currently in a git repo:

    # Git status
    set git_branch (git rev-parse --abbrev-ref HEAD ^ /dev/null)
    if test "$git_branch" != ""
        set_color white
        printf " {"
        set_color cyan
        printf "git $git_branch"
        set_color white
        printf "}"
    end

This specifically checks to see if the $git_branch returns anything. If not, it just skips this section.

Then, the current directory:

    set_color green
    printf " %s" (string replace $HOME '~' (pwd))

This also replaces my home directory with ~, which is fairly standard and makes paths shorter (although with a two character username, this isn’t an issue on most systems).

Finally, close off the prompt on the next line:

    # Actual prompt
    set_color white
    printf "\n\$ "
end

Probably start off a holy war with my two line prompt, but it’s been working great for me, so … that’s what I use. 😄

Here it is in all it’s colorful color:

As an added bonus, I like to have one extra empty line after the prompt and before the command output (as you may have noticed). You can do that by defining another function:

function whitespace_after_prompt --on-event fish_preexec
    printf "\n"
end

I have this in my main Fish config file (~/.config/fish/config.fish), but it could theoretically be in a function file as well.

And that’s it. The entire function (and the rest of my configs) are available on GitHub if you’re so interested. If you’d rather Zsh (which I used prior to Fish), my config for a very similar prompt is still available on GitHub as well.