New features in the fish shell
Fish (the "friendly interactive shell") has the explicit goal of being more user-friendly than other shells. It features a modern command-line interface with syntax highlighting, tab completion, and auto-suggestions out of the box (all with no configuration required). Unlike many of its competitors, it doesn't care about being POSIX-compliant but attempts to blaze its own path. Since our last look at the project, way back in 2013, it has seen lots of new releases with features, bug fixes, and refinements aimed at appealing to a wide range of users. Some of the biggest additions landed in the 3.0 release, but we will also describe some other notable changes from version 2.1 up through latest version.
The first release of fish was made by Axel Liljencrantz in February 2005. Version 2.0 was released in September 2013, and 3.0 in December 2018. The latest version, 3.1.2, was released on April 29, 2020.
Logical operators
Prior to the 3.0 release, fish lacked a special syntax to express logical AND (&&) and OR (||) operations. Instead, the and and or commands were used for such operations. These commands verify the previous command's exit status before acting accordingly. For example, command1; and command2 expresses a logical AND operation in which command2 is executed if, and only if, command1 returns an exit status of zero.
This syntax proved to be unpopular among converts from other shells where operators such as &&, ||, and ! were used to express logical operations and it was the subject of several discussions on the project's issue tracker. Due to popular demand, these logical operators are now supported as of fish 3.0 to make it easier to migrate from other shells. It is now possible to enter command1 && command2 in the shell or in a script to express the logical AND operation.
History and incognito mode
When a command is executed in the shell, it is inserted at the end of the history file located at ~/.local/share/fish/fish_history, accompanied by the timestamp of its entry. It is also possible to save a command to the history without executing it using the Alt+# binding (introduced in version 2.3.0). It toggles the current command line between commented and uncommented states. As with Bash and other shells, the up or down arrow keys can be used to search forward or backward in the history.
One nice feature for fish history is the ability to filter through the history based on what is typed into the shell. Entering a few characters and then hitting the up arrow key performs a history search for the commands that include the typed characters. For example, typing git and hitting the up arrow will display only the commands that contain the string git.
Fish does not enter any command that begins with a space into its history file, essentially treating it like an incognito command. This often surprises users who are transitioning to fish from Bash or other shells that do not have this behavior; it is also easy to activate it unintentionally when pasting commands to the terminal from other sources. There have been several discussions in GitHub issues on whether to retain this behavior in future releases and how to make it more discoverable. As of fish 3.1.2, no changes have been made to this aspect of the shell behavior.
A new addition in fish 3.0 is the --private flag that can be used to start fish in private mode; it stops all subsequent commands from being logged in the history file. This is handy for when a user wants to enter sensitive information in a command. The $fish_private_mode variable was also added for the purpose of detecting if a script is being run in private mode so that its behavior may be modified accordingly to respect the user's wish for privacy.
Aliases and abbreviations
Like many other shells, fish provides the ability to define memorable alternate names (known as aliases) for commonly used commands. Aliases can help to eliminate a great deal of typing when working in the command line. There are two ways to create aliases in fish. The first involves creating a function wrapping a command:
function gc git commit $argv end
The second option is to use the alias keyword, which is essentially a shell wrapper for the function syntax above. When using this syntax, the $argv arguments string will be appended automatically.
alias gc="git commit"
The release of fish 2.2 brought support for abbreviations to the shell. These are similar to standard aliases, but expand inline to the full command when typed. For example, a command can be shortened to yul using the following:
abbr yul "yarn upgrade --latest"
Once yul is entered into the terminal followed by the space or enter key, it will be expanded into the full command. By default, abbreviations are stored to the universal scope, so they immediately become available in all current fish sessions and subsequent ones as well. To make an abbreviation visible in the current session only, use the (surprisingly named) --global flag when creating it. This places the abbreviation in the global scope which is local to the current shell. Abbreviations are generally better than aliases because the full command can be seen before executing it, which makes it easier to edit for a one-off change. They are also more suitable for interactive demos or presentations because the instructor can use a shortcut without obscuring the full command.
Vi mode
The default key bindings used by fish for moving the cursor around on the command line are from the Emacs editor. Examples are Ctrl+A to move the cursor to the start of a command, Ctrl+E to move to the end of a command, and so on. Fish 2.2 introduced a Vi mode for those who prefer bindings from the Vi editor. It supports command mode, insert mode, and visual mode bindings. Fish also provides shared bindings that are accessible regardless of your preferred mode.
If neither mode is sufficient for a particular edit, an external editor may be summoned using the Alt+E or Alt+V shortcuts. The two commands are synonymous; the editor is chosen from the first available of the $VISUAL or $EDITOR environmental variables.
Clipboard integration
Fish features an Emacs-style kill ring for blocks of text that were previously killed. For example, Ctrl+U cuts from the cursor position to the start of the line and inserts the text into the kill ring while Ctrl+Y pastes the latest value from the kill ring at the cursor position.
A change in Fish 2.4 is that any text cut with kill ring commands no longer overrides the system clipboard. This behavior was changed because it was a common source of frustration when pasting to the shell from external sources because cutting some text would override the previously copied text meaning that it would have to be copied again. Working with the system clipboard is still supported on Linux (X11 and Wayland) and macOS. Ctrl+X is used to copy the whole line to the clipboard (regardless of cursor position) and Ctrl+V pastes from the clipboard to the shell.
Web configuration tool
The web configuration tool, launched with fish_config, provides a nice way to customize the prompt style, color theme, and so on. An important security enhancement landed in 2.1.1 which prevents a remote-code-execution attack when running this web interface by using an authentication token to protect requests and responding only to requests that include this token.
In addition, the behavior of this tool was changed slightly in version 3.1 by causing it to display a list of the commands it is executing, for debugging purposes. Other improvements include new theme presets, base-16 color options, prompt styles, and support for viewing, editing, and adding abbreviations.
Wrap it up
Fish was originally implemented in C but is now primarily written in C++. It does not follow any particular release cycle but major versions usually take a few years to come out. The best way to follow the development of the shell is through its GitHub repository and official mailing list. The changes that are being planned for upcoming releases are detailed on the milestones page on GitHub. An official Gitter channel also exists for community discussions.
Overall, the refinements being made continue the process of making fish easier and more convenient to use compared to other shells. It strikes a good balance between having useful defaults to get started with immediately and leaving room for extensibility and customizability. A tutorial is available for those looking to quickly get up to speed with the precise differences between fish and more traditional shells.
Index entries for this article | |
---|---|
GuestArticles | Isaiah, Ayooluwa |
Posted Sep 29, 2020 18:18 UTC (Tue)
by ibukanov (subscriber, #3942)
[Link] (4 responses)
Posted Sep 30, 2020 17:54 UTC (Wed)
by glasserc (subscriber, #108472)
[Link] (3 responses)
Posted Sep 30, 2020 23:19 UTC (Wed)
by johannes (guest, #116140)
[Link] (1 responses)
Surprisingly, a condition for if/while consists of a command plus a tail of and/or commands, see https://github.com/fish-shell/fish-shell/blob/master/src/....
if cheap-condition; or expensive-condition-part-1; and expensive-condition-part-2
Although as a developer I agree that && and || look better.
Posted Oct 1, 2020 12:36 UTC (Thu)
by widowmaker (guest, #142307)
[Link]
Posted Oct 2, 2020 23:21 UTC (Fri)
by qlyoung (guest, #142338)
[Link]
Posted Sep 29, 2020 19:34 UTC (Tue)
by logang (subscriber, #127618)
[Link] (14 responses)
Also, the article is wrong about incognito mode. Bash does not add commands that begin with a space to the history and has done so for as long as I can remember. It's just not a feature that's well known -- I only found it by accident.
Posted Sep 29, 2020 19:44 UTC (Tue)
by skx (subscriber, #14652)
[Link] (3 responses)
Bash does allows you to ignore history entries that begin with a space, but you need to set the
Posted Sep 29, 2020 19:49 UTC (Tue)
by logang (subscriber, #127618)
[Link] (2 responses)
Posted Sep 30, 2020 13:39 UTC (Wed)
by Paf (subscriber, #91811)
[Link] (1 responses)
Posted Sep 30, 2020 22:27 UTC (Wed)
by sjj (guest, #2020)
[Link]
Posted Sep 29, 2020 19:47 UTC (Tue)
by jake (editor, #205)
[Link] (1 responses)
hmm, it does for me (add commands that start with a space to the history) ... i haven't investigated why but it is not something i (knowingly) configured ...
jake
Posted Sep 29, 2020 19:52 UTC (Tue)
by dtlin (subscriber, #36537)
[Link]
Posted Sep 29, 2020 19:54 UTC (Tue)
by mathstuf (subscriber, #69389)
[Link] (3 responses)
Posted Sep 30, 2020 12:13 UTC (Wed)
by andrewsh (subscriber, #71043)
[Link] (2 responses)
Posted Sep 30, 2020 12:35 UTC (Wed)
by mathstuf (subscriber, #69389)
[Link] (1 responses)
Posted Sep 30, 2020 14:47 UTC (Wed)
by ayoisaiah (guest, #141800)
[Link]
Posted Sep 29, 2020 22:05 UTC (Tue)
by gus3 (guest, #61103)
[Link] (2 responses)
Posted Sep 29, 2020 22:25 UTC (Tue)
by mattdm (subscriber, #18)
[Link]
Posted Oct 3, 2020 6:33 UTC (Sat)
by marcH (subscriber, #57642)
[Link]
"\e[B": history-search-forward
... in /etc/inputrc.
Posted Sep 30, 2020 1:23 UTC (Wed)
by zanchey (guest, #94250)
[Link]
Posted Sep 29, 2020 21:10 UTC (Tue)
by nash (guest, #50334)
[Link]
Posted Sep 29, 2020 22:15 UTC (Tue)
by areilly (subscriber, #87829)
[Link] (2 responses)
Posted Sep 30, 2020 5:21 UTC (Wed)
by johannes (guest, #116140)
[Link] (1 responses)
> All of the UI stuff was lovely, but I don't see that that has to imply a different shell language
True but it's much easier to implement consistent highlighting, indentation, completion and error messages if the language is syntactically simple. Other shells don't indent or highlight at all.
> I couldn't get it to behave sensibly around remote ssh commands (with fish as the login shell on the remote machine).
Yeah, that's one of the places where setting fish as login shell can backfire. It's often better to launch it in a different way.
Posted Sep 30, 2020 10:31 UTC (Wed)
by mathstuf (subscriber, #69389)
[Link]
I agree. I use zsh almost exclusively, but my login shell is bash. I configure my terminal-launching keybindings and tmux to use zsh by default instead.
Posted Sep 29, 2020 22:38 UTC (Tue)
by miquels (guest, #59247)
[Link] (2 responses)
Posted Sep 30, 2020 6:11 UTC (Wed)
by fwiesweg (guest, #116364)
[Link] (1 responses)
Posted Nov 3, 2020 15:52 UTC (Tue)
by nix (subscriber, #2304)
[Link]
I don't think it's a good login shell but for popping into to dig around at structured text it's ever so much nicer than sed/awk oneliners a lot of the time.
Posted Sep 29, 2020 23:13 UTC (Tue)
by randomguy3 (subscriber, #71063)
[Link]
Posted Sep 30, 2020 0:28 UTC (Wed)
by flussence (guest, #85566)
[Link] (8 responses)
Huh. I'd gotten so used to putting HISTCONTROL=ignoreboth in .bashrc on every system that I forgot the space behaviour wasn't a default there.
Posted Sep 30, 2020 13:42 UTC (Wed)
by Paf (subscriber, #91811)
[Link] (7 responses)
Posted Sep 30, 2020 14:09 UTC (Wed)
by randomguy3 (subscriber, #71063)
[Link] (1 responses)
Posted Sep 30, 2020 20:38 UTC (Wed)
by mathstuf (subscriber, #69389)
[Link]
Posted Sep 30, 2020 20:06 UTC (Wed)
by szm (subscriber, #100120)
[Link] (3 responses)
rm -rf ./*
or dropping database tables, etc. from the search history. Before learning about history suppression in my younger years (i.e. back when I tended to be prone to getting nervous during an incident with a critical system or when I sometimes would grow impatient while working over high-latency ssh connections), I've had several jump-scares when, by "typing ahead" not only commands but retrieving items from the history with the arrow keys, found that I accidentially ended up with the "interesting side-effects if run here and now" kind of command. Luckily always before hitting enter, but still.
It's not a fool-proof system, but I find it quite useful in the "better safe than sorry" kind of way.
Cheers,
Posted Oct 1, 2020 0:02 UTC (Thu)
by Paf (subscriber, #91811)
[Link]
Posted Oct 3, 2020 6:44 UTC (Sat)
by marcH (subscriber, #57642)
[Link] (1 responses)
Agreed 200%: I recalled by mistake commands like these from my (crazy long) bash history once or twice and now I'm trying hard to never let them get there in the first place.
Another trick I use (better safe twice than sorry) is this:
rm -rf ../current_directory/*
With completion it takes barely longer to type and it greatly reduces the chances of that command doing anything if recalled by accident.
Finally, there is of course:
mv * ~/Trash/
Only the last one would probably have stopped me from recursively deleting the target of a symbolic link while thinking I was just deleting the link itself...
Posted Oct 3, 2020 17:51 UTC (Sat)
by jo42 (subscriber, #59640)
[Link]
zshaddhistory()
Posted Oct 3, 2020 6:46 UTC (Sat)
by marcH (subscriber, #57642)
[Link]
You could start by explaining why you dislike the feature, I'm curious :-) Lots of copy/paste?
Posted Oct 1, 2020 11:38 UTC (Thu)
by nelljerram (subscriber, #12005)
[Link] (1 responses)
Posted Oct 9, 2020 2:06 UTC (Fri)
by beren (guest, #142429)
[Link]
Posted Oct 3, 2020 6:51 UTC (Sat)
by marcH (subscriber, #57642)
[Link]
More like the readline bindings used in many other command lines and not specific to Emacs.
New features in the fish shell
I'm a long-time fish user who feels the opposite way. I really like the regular syntax of the fish shell language (no New features in the fish shell
[[
, then
, fi
, esac
) and introducing special syntax for and
and or
detracts from that. The linked discussion on Github also has additional arguments in both directions -- for example, &&
and &
function very differently despite their apparent visual similarity; but also, complicated boolean conditions in if
statements in fish
are a little clumsy because only the first command (until the first semicolon) is the condition, so &&
helps mitigate that.
New features in the fish shell
This means you can write chained conditions like this:
# body goes here
end
Using begin/end for grouping allows for more complex conditions.
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
HISTCONTROL
variable to ignorespace
.New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
"\e[A": history-search-backward
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
The use case is different, so different tools are justified. The most used interactive commands work the same way in fish.
There are easy-to-learn fish equivalents for constructs that are commonly used interactively. If something is missing it can usually be implemented in a small function.
Supporting obscure features from other shells yields diminishing returns, but widely used features are happily added, like && and var=value.
Likewise, humans use different constructs in oral and written language, both languages co-evolve and adopt useful features from each other.
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell
szm
New features in the fish shell
New features in the fish shell
New features in the fish shell
{
# Don't save run-help and kill commands and commands run in bad-stuff
[[ $1 != (run-help *|kill *|bg|fg|cd|reboot|halt) && $PWD != $HOME/bad-stuff/* ]]
}
New features in the fish shell
New features in the fish shell
New features in the fish shell
New features in the fish shell