Fish: A user-friendly command line shell for macOS, Linux, etc
fishshell.com
fishshell.com
We are in the process of scoping a major release (fish 3.0) that can include major syntactic changes. If there's an aspect of fish you would like to see changed, now is a great time! Of course we'll read the comments here, and our issues page [1] is where discussion happens.
We're also welcoming new contributors. It's easy to get started writing completions or with the issues labelled "easy-pick". The core code base is reasonably modern C++ too.
I implore you to NOT use JSON or ProtocolBuffers, neither are appropriate formats for configuring my shell, or storing long sets of textual data. Protocol Buffers is hardly the kind of format I want to parse and read my history in, and JSON is a less than ideal configuration format for many reasons.
https://github.com/vstakhov/libucl UCL (Universal Configuration Language) is a much more appropriate format for configuring programs than JSON, and is has been accepted as standard for use configuring all tools included in the FreeBSD operating system.
I also suggest you consider using a different language to store shell history, since shell history and shell configuration are two very different jobs with different goals and trade offs. I personally would suggest a simple safe format like TOML https://github.com/toml-lang/toml for storing the history.
But If you do want a single format for both configuration and history, I would implore you to pick TOML not JSON or Protocol Buffers.
TOML is definitely an interesting choice if a text format is deemed necessary however. Array of tables syntax should be a fine syntax choice for storing history.
I totally drank the SQLite koolaid a long time ago.
Protobuf (or any other binary format) has the benefit of being faster to load, but I agree - it should not be be used to store things like history. It should be easy to parse history without having to write code. And with Protobuf, you need the schema.
If a binary format is going to be used, it should be optional - or perhaps there should be tools to convert the history file to/from a plaintext format.
Also since he’s the kind of person that a HN reader will probably Ike to know more about. The original author of the aforementioned uclcmd, Allan Jude [http://twitter.com/allanjude], is the former host of the TechSNAP podcast [http://www.jupiterbroadcasting.com/show/techsnap/], current host of the BSD Now podcast [http://www.jupiterbroadcasting.com/show/bsdnow/], a current member of the FreeBSD Core team, and co-authored not one but two books on the ZFS file system FreeBSD Mastery: ZFS [https://www.amazon.com/FreeBSD-Mastery-ZFS-7/dp/0692452354/] and FreeBSD Mastery: Advanced ZFS [https://www.amazon.com/FreeBSD-Mastery-Advanced-ZFS/dp/06926...]
https://stedolan.github.io/jq/manual/
Both are single binaries, so easy to run them where you need it. I don't know comparable stuff for the other mentioned formats, whenever I need to deal with yaml in scripts i run
https://github.com/bronze1man/yaml2json
I would be totally fine with any format that can be converted to JSON on the fly. Of course I would love to have something like these tools more integrated within a shell, but the representing text format on the disk is not that important for me.
Thank you for leading the charge on modern command line design.
Thanks for both your work on fish and for your blog!
That and support for
SOME_ENV_VAR=foo ./bar
(not having to prepend "env ") are what's stopping me from recommending fish to others.Btw: Big thank you for fish, it's awesome!
cd build && LD_LIBRARY_PATH=../lib ./foo
:/It's no dealbreaker though, I still use fish and love it :) Just a minor annoyance ;)
$(subcommand)
as an alternative to (subcommand)
PS: I have been using fish as my primary shell for about a year now, very happy with it. :)With ZSH, I can run a command with a space at the start so it's excluded from history, even though its not saved to disk, the last command is still stored in memory so I can press Up to access it again.
This is handy when a mistake is made in the command means I need to run it again with some small change.
Maybe adopt a package manager for plugins too? ;)
Unlike bash, fish even expands it in place, allowing you to make changes.
``` function sudo if test "$argv" = !! eval command sudo $history[1] else command sudo $argv end end ```
This works for `sudo !!`
https://github.com/srynot4sale/dotfiles/blob/master/fish/fun...
Only one more key press than "sudo !" :)
but similarly, fish user for the last 2+ years.
In fish Alt+Up does something similar, but repeated presses iterates over all words of all commands rather than last words of bash commands. A big improvement is you can type some chars first and then Alt+Up only gets words matching this substring — like Up but word granularity! (Closest bash key is Ctrl+Alt+I)
If you don't mind the differences, and want Alt+. muscle memory to work in fish too, do:
bind \e. history-token-search-backward
(plus Alt+Up doesn't work for me in linux console, Alt+. does)1. equivalent of `set +x` and `set +e` from bash (to echo commands and stop script execution on the first non-zero return) – https://github.com/fish-shell/fish-shell/issues/3427,
2. Associative arrays – https://github.com/fish-shell/fish-shell/issues/390
e.g. apt install foo # you need root permissions to install, won't work sudo !! # will expand to sudo apt install foo, and it'll work!
What I don't like is that after hitting Tab twice when doing path completion, the list of possibilities comes out, allowing arrow keys to be used for selection. The problem is that it requires pressing Enter to confirm the suggestion and I fear of executing the command with incomplete path prematurely (it's just one more Enter hit away).
We're open to a lot of things (except "make it fully POSIX-compatible"). We're still trying to not massively break everything, though.
The sort of things we've discussed include:
- Not expanding `{}` so you don't need to quote it with e.g. `find -exec {}` (currently this is read as a zero-element brace expansion, i.e. it expands to no argument)
- Removing the `?` single-character glob because it's apparently entirely unused and is another special character to remember (in fact we've also talked about removing `^` as a shorthand for redirecting stderr and `%something` process expansion)
- Allowing `$(command)` command substitutions, which could be used inside quotes as well (long term, these would replace our current `(command)` style)
- Possibly removing the special handling we have for $PATH, $CDPATH and $MANPATH (these are treated by fish as lists, which is nice in some ways and causes pain in others)
For a general overview, see [the fish 3.0 milestone on github](https://github.com/fish-shell/fish-shell/milestone/18).
Other than that, my biggest pain point with fish now is that it doesn't interact well with pkg-config (issue #982) and the workarounds for that are a bit awkward. eval is scary and may require double-quoting the non-pkg-config parameters, and string splitting can wrongly split inside quoted text.
* The main things I like about fish are predictive history completion and syntax highlighting, both of which work about the same with a pair of zsh plugins;
* The syntax occupies what is, for me, an awkward middle ground: it's a bit better than POSIX syntax if POSIX shells didn't exist, but not enough better that I care to juggle the two syntaxes;
* I don't like that fish tries to be "helpful" in certain cases such as lying to you about the delimeter actually used by $PATH.
I have a couple of specific gripes about the syntax, which aren't really inherent issues so much as unimplemented features.
I think if I was going to deviate from a POSIX shell, the alternative would have to be more better than fish to be worth it.
if [[ $- == *i* ]]
then
bind '"\e[A": history-search-backward'
bind '"\e[B": history-search-forward'
fiThis. It's an awesome shell, but I couldn't set is as a default because my already done shellscripts and aliases would stop working. And I also can't set it on server, because then a deployment tool logs in, and expects bash.
And if it isn't default, I might not bother, I'd forget to run it manually most of the time.
(The worst offender is replacing && with 'and' - why not at least enable both and preserve compatibility?)
* zsh-syntax-highlighting: https://github.com/zsh-users/zsh-syntax-highlighting
* My zshrc, for what it's worth: https://github.com/burke/dotfiles/blob/master/.zshrc#L173-L1...
really, really cool
I’ve never had issues as a somewhat heavy user.
One thing that can be particularly annoying is that it seems to be impossible in fish to evaluate a command in a subshell and embed its output into a string without assigning it to a variable first.
ARGV handling is a little weird too. I understand the impulse to get rid of $1, $2, $3, $*, $@, $#, etc., but it ends up just requiring different gymnastics, like writing `set -e argv[1]` instead of `shift.
Running on a POSIX system, there are decades' worth of shell snippets out there which don't work with fish, but do with sh, ksh, bash & zsh.
Fish is just gratuitously incompatible. Having fish-users on a team is a pain, because they'll write everything in fish and either not bother producing sh equivalents, or produce insufficiently-tested sh equivalents. It's just a lot of pain and bother, and what's the benefit? Zsh does everything fish does and more, so why not just use it?
Edit: I just wish that the original fish developers had chosen to implement all their cool features and be sh-compatible, or at the very least had exercised some wisdom in where they chose to break compatibility. Instead, it very much feels like a 22-year-old's project: full of ideas, some good & some bad, and needlessly without respect for the past. I've no problem with rejecting the past when the past is broken (and sh is broken in places); my problem is with the equivalent of saying, 'hey, English spelling is terrible, soh Aiv dəseidid tū dəvelup mai ohn.'
Where stuff like fish is a pain for me is with apps that for whatever reason need shell integration (virtualenv, for example). Non-POSIX shells end up being an afterthought there, so you end up with no or buggy support, and end up having to find some other custom variant (virtualfish, etc.) to get good behavior. Sticking with a more mainstream shell tends to be safer there, and zsh barely makes it over that line.
Some highlights:
* It comes with helpful interactive features, like syntax highlighting and on-the-fly error checking; enhanced history searching with ^R; directory history and a Ranger-inspired "navigation mode" (see homepage for demos).
* It supports some advanced (in shell standards) programming concepts, like lambdas, namespaces, nestable maps. It also has some pretty powerful API for interactive features. These two aspects work hand in hand and offers quite some customization possibilities. In fact, one user implemented interactive history expansion (!! !$ !1) himself: http://zzamboni.org/post/bang-bang---shell-shortcuts-in-elvi...; his configuration file is also worth readng: http://zzamboni.org/post/my-elvish-configuration-with-commen....
* A lightweight builtin package manager is in the work.
* It is written in Go and comes as a statically linked binary.
* A native Windows port is under way, in case you care :)
Good points:
* Fish's completion sure saves a lot of typing
* `brew install fish` on osx
* Config in ~/.config/ rather than cluttering up the root of your homedir
* Can change many settings without needing to edit config directly - eg. `set -U fish_user_paths $fish_user_paths ~/path/name`
Bad / not intended for:
* It's not for scripting. Just use bash for that.
* I've found appending to env vars to be fiddly. Maybe I'm doing it wrong
* pyvenv's bin/activate.fish was broken for a long time (not fish's fault!)
Similarly, if you want your code to be portable, bash is a safer bet.
Of course, if your scripts only run on your own machine (which is a huge if), then fish is probably better.
If nothing else, I have thousands of lines of my own shell scripts that I don't care to move to a different shell syntax that's not obviously better :)
I'm designing a new shell language which bash can be auto-converted to. This is a lot of work and the plans might change, but the beginnings of it are here:
I will never install it on any remote system, because it doesn't feel right to force a shell to other remote users.
Also how much time do you need something that fancy on a remote prompt? The nice features of bash like [Ctrl-r] works everywhere and the shortcuts ( Ctrl-a, etc. ) are usually the same.
The power that zsh gives me is all about my local dev machine. Things like utilities command completion or `scp remote-server:~/ [tab]` to find a file on a remote machine, or the countless other extras are mostly useful on my local.
You know users can choose their own shell with chsh, right?
I know I could just invoke bash, but prefixing commands with "bash -c '" is tiring and papercuts are why I switched to fish in the first place. I wish bash had a sane auto-complete prediction like fish.
I use fish interactively and write very simple scripts in bash and the more complex in Python.
You need to learn a few idioms like: replace $[1+1] with (math "1+1"); replace
> for f in $(ls); do echo $f; done
with
> for f in (ls); echo $f; end
I should start a Rosetta Stone of Bashisms to Fishisms - I'd be interested if anyone has got Android Open Source build environment / lunch working.
If nothing else, I have thousands of lines of my own shell scripts that I don't care to move to a different shell syntax that's not obviously better :)
There are lots of posts on the blog about it, but here is an example:
"OSH Runs Real Shell Programs" - http://www.oilshell.org/blog/2017/07/02.html
Another comment: https://news.ycombinator.com/item?id=15916470
However one thing is that it's not yet a good interactive shell. It's mainly treating bash as a programming language. However I think that is a good foundation for an interactive shell. Some details here:
https://www.reddit.com/r/commandline/comments/7c3f9f/osh_02_...
"\e[A": history-search-backward # Up key
"\e[B": history-search-forward # Down key.
Allows you to get press up while you have `ssh 10.2` in the terminal to cycle through ips matching, etc. It won't display like in fish before pressing it, but I find that's a reasonable tradeoff.
Unfortunately, although it is fully scriptable, writing scripts in Fish feels a little clunky at times. For example, I wanted to test if $dest exists, and if not, whether $src is newer than $dest. Since there are no && or ||, parentheses are used for command substitution, and the Fish version of "test" has no -nt to check if a file is newer, I have to do this:
if begin; not command test -f $dest; end; or begin; command test $src -nt $dest; end
# do something
endHappily the begin/ends in ifs are no longer necessary:
if not command test -f $dest
or command test $src -nt $dest
# stuff
end
In this particular case, you can simplify it further with test's operators: if command [ ! -f $dest -o $src -nt $dest ]
#stuff
end
which is comparable in length to bash, though other cases will certainly be longer.What I ended up with was (for a shortcut for generating a password on the command line):
function pw --argument length
test -z $length; and set length 16
python3.6 -c "import secrets; print(secrets.token_urlsafe($length))"
end not command test -f $dest
if or command test $src -nt $dest
# do something
end
It saves on the begin ... end, but is confusing to look at. Nested conditions are really not very ergonomic in fish.My original reason for using fish was onboarding junior engineers. I had a pretty fancy zsh/emacs setup, and they'd ask me how to make their shell do that (reminder: on macOS you get bash3.2 by default!). So I switched to fish/spacemacs, so the answer isn't "uh sure you can rsync 50MB of accrued elisp".
Result: I have a nice zplug[0] setup now:
> cat /usr/local/opt/zplug/packages.zsh
zplug "zsh-users/zsh-syntax-highlighting"
zplug "zsh-users/zsh-history-substring-search"
#zplug "zsh-users/zsh-autosuggestions"
zplug "zsh-users/zsh-completions"
#zplug "plugins/git", from:oh-my-zsh
#zplug "plugins/github", from:oh-my-zsh
#zplug "plugins/fossil", from:oh-my-zsh
#zplug "plugins/docker", from:oh-my-zsh
#zplug "plugins/docker-compose", from:oh-my-zsh
#zplug "plugins/python", from:oh-my-zsh
#zplug "plugins/pip", from:oh-my-zsh
zplug "plugins/lein", from:oh-my-zsh
#zplug "plugins/autojump", from:oh-my-zsh
#zplug "plugins/command-not-found", from:oh-my-zsh
#zplug "plugins/gpg-agent", from:oh-my-zsh
#zplug "plugins/ssh-agent", from:oh-my-zsh
#zplug "plugins/thefuck", from:oh-my-zsh
#zplug "plugins/aws", from:oh-my-zsh
#zplug "plugins/osx", from:oh-my-zsh
#zplug "plugins/battery", from:oh-my-zsh
zplug "tweekmonster/nanofish", as:theme
Still easy to reproduce. Maybe consider some of those plugins. You don't have to give up being POSIXy. Whether or not that's good or bad, I leave up to you :)(Commented out bits are things I'm playing with: mostly to improve shell startup time.)
It's a Python-based shell that attempts to be somewhat bash-compatible (for ease of use over, e.g., IPython). Instead of making a new scripting language (fish), go all in.
Pros: scripting is amazing. ( while True: ls ~ ) Cons: can be a bit slow (because Python interpreter).
oh-my-fish [1] plugins is a good place to see (part of) what is available
I also use it on my home computer (on top of zsh) and am pretty happy with it.
It is a bit slower in terms of response time, but that's OK. Most of the problems I had with it have been resolved after filing bug reports. The only annoyance is that when I quit Midnight Commander, I'm back in the directory I started with. But that's a MC problem, and not xonsh. Every shell out there has a special wrapper script to handle MC. I just need to understand the one zsh uses and port it over to xonsh.
The two best parts about xonsh:
1. You can use the command line like you would a Python interpreter. Need to do a quick calculation? Just type it in and press Enter.
2. No more obnoxious bash/zsh scripting. Write all your shell scripts in Python!
eval (cat ~/.local/share/fish/fish_history | grep 'cmd:' | cut -c8- | fzf --tac)
and then I simply bound it with `\cr`. It doesn't exactly replace Ctrl-R functionality but it suits my need.This in turn means I’m willing to install it with no hesitation in all my local and remote logins, so I have a uniform simple setup everywhere.
One issue is the behavior of globs. I often want to do "scp host:directory/* ." but you can't do that in fish, so I drop to bash. That's pretty much all I drop to bash for.
Like fish, it'll interpret an unquoted asterisk (sorry HN formatting) as the glob character. So it tries to match pathnames to `host:directory/`.
Only the default bash behavior (if "nullglob" and "failglob" aren't set) is to pass the literal glob expression if no match could be found.
So once you `mkdir host:directory; touch host:directory/file` (which you can do - this is unix, so almost all pathnames are allowed), this will be broken.
This means you _should quote the token_ in bash, which is exactly what you should do in fish.
Note: This is also why you might see code like the following in bash:
for file in *; do
if test -e "$file"
See http://mywiki.wooledge.org/BashFAQ/004 and http://mywiki.wooledge.org/glob#nullglob for more info. scp 'host:directory/*' .I think what convinced me was the last point: if you really can't do what you're trying to do in Fish, firing up sh/csh/ksh/bash/python/xonsh costs virtually no effort.
But of course having to recall how to do something in a POSIX-compatible shell can cost if you've been using a POSIX-incompatible shell for years. Memory and experience do fade over time.
I’m certainly guilty of finding one-liners online which frequently fail in fish, which puts me in the position of either actually learning what the terse statement actually does and re-implementing it in the fish “c-like” syntax, or I just drop into bash then get on with my business.
Really happy with it, but I know I get sideways stares from graybeards and gurus who consider it the small-batch, fixed-gear, “Crocs” of shells.
I don't understand what the fuss with not being compatible with bash is. Bash scripts run with #!/bin/bash in bash. If you need to write/copy&paste some bash code just jump to a bash shell. In the meantime, fish will save you lots of time on your daily regular shell usage.
1. Support for last command by !!, as in `sudo !!`.
2. Ability to search history from a partial command like `git checkout`, where up arrow would step backward from the present.
I wish all my tools were as friendly as fish!
We have that feature, and it's enabled by default. Just type the partial commandline and press up-arrow. Or do you mean something slightly different?
$ ls -la foo
$ ls -la bar
$ ls -la /
When I type `ls -l` and hit the up arrow, my first result is `ls -la bar` not `ls -la /`. This is a small problem but sometimes annoying, especially after using other shells that work differently.For improvements here are two things I wish where possible in fish:
* The ability via a flag to say that a fish-function should not interpret passed function-arguments. This would enable us to pass e.g. URLs to a function without quote them, which is somewhat anointing right now. So this change would be a nice usability improvement.
* I would like to be able to write dynamic completions, containing of a key-value pair. The key should be displayed and if the user selects a completion-item, the corresponding value should be passed to a fish-function from which arbitrary actions could be triggered. Maybe supporting JSON input here would be nice too.
Note that functions already don't expand the passed arguments. Fish expands them _before_ executing the function, so what you are asking for is a major layering violation.
E.g. you can do this:
function a
b $argv
end
function b
string escape -- $argv
end
a 'something * with ? loads {} of $pecial char"acters'
and the output will come out right. You only need to quote it once.>This would enable us to pass e.g. URLs to a function without quote them
I'm assuming what you are asking for isn't _passing_ URLs to functions, but rather _pasting_ URLs into the commandline.
In which case fish has a feature I like to call "magic paste". When you paste with an open single quote (`somecommand '`), it'll escape single quotes (and backslashes). So you type the closing single quote and your argument will be passed to the command you are calling as the exact text you pasted.
>The key should be displayed and if the user selects a completion-item, the corresponding value should be passed to a fish-function from which arbitrary actions could be triggered.
We have "descriptions" for completions, which will be displayed _alongside_ the argument (i.e. the "value"). To generate them dynamically, simply separate them with a tab character (`\t`) from the argument, and separate these pairs with newlines.
So
printf '%s\t%s\n' 'argument' 'description' 'argument2' 'description2'
will be displayed as argument (description) argument2 (description2)
We use this to great effect in numerous places. E.g. the git completions will offer commit hashes with the summary line as description, so when I type `git revert <TAB>` I get something like 0a129475 (Replace opts.stdout with opts.to_stdout)
I'm not sure I like the idea of hiding the real argument from the user.>Maybe supporting JSON input here would be nice too.
The best completions are those that actually call the stuff they are completing (because that makes them really reactive), and not many commandline tools actually generate JSON. Anything in particular you're thinking about here?
(And now I'm hoping that HN doesn't hopelessly mangle this comment)
The idea would be to be able to write something like this:
function yt --pure
youtube-dl $argv
end
The parser should stop interpreting arguments after the yt function call and pass the args as strings to the function as before.Then such a call would be possible: yt https://www.youtube.com/watch?v=mqma6GpM7vM
# Second Topic
I wrote a little utility [1] to quicker navigate on the file system. Right now I have two commands:
m [folder] // goto highest ranked folder
mm [folder] // list results for folder-keyword
I would like to be able to write 'm foobar' and then press tab to get a vertical completion-list. When the user selects a completion-list-item I would like to pass the result-path to the cd command.
Some time ago I tried to write such a completion, but failed – that's why I still use a separate mm-function to list search results.[1]: https://github.com/thibran/maybe
I think JSON might be a good choice here because it would simplify the interaction with the completion program for advanced completions. Passing all those flags – mixed with some other shell function calls – to the 'complete' program is a bit cumbersome. My dream would be to have something like 'complete -c maybe --json [maybe-flag-to-generate-json-output]' and handle everything inside the application.
{
"fish_completion_version": 1,
"header": "optional, completion header",
"orientation": "vertical",
"on_single_result": "optional, choose-item or display-item",
"arguments": [
{
"item",
"description",
"optional return value, if empty return item"
}],
"item_handler": "optional, name of fish function to call on-item-chosen"
}
I know JSON is not something you want to assemble in a script, but all programming languages have support for it and it's easy to use.Okay, so I did understand what you are saying.
Like I said, that's a rather large layering violation, since the function has nothing to do with the expansions happening on its arguments. They are expanded before they ever reach the function. It's essentially allowing functions to modify the syntax around them.
How exactly would this treat e.g.
yt $URL/somefile
Would the variable still be expanded? Would an asterisk glob be? If I _did_ want it to expand something even though the function is declared "pure", how would that work?Now I couldn't be sure how my arguments would be interpreted, or I'd have to go checking for everything I invoke.
I use youtube-dl myself quite often, and what I do is just type
youtube-dl '
then press ctrl-v (which is a keybinding that uses xsel or pbpaste to get the clipboard contents and insert them into the commandline) or ctrl-shift-v (which is my terminal's paste binding). Then I press `'` again and can download the video.Additionally, it's looking like 3.0 will remove the `?` glob, which is the thing that is most annoying with URLs (though "&" is also a thing).
> I would like to be able to write 'm foobar' and then press tab to get a vertical completion-list. When the user selects a completion-list-item I would like to pass the result-path to the cd command.
I would assume the reason you failed is that the argument you want doesn't match the argument the user typed. Because fish's completions undergo filtering (with some fuzzy-matching, so e.g. `f_c_i` matches `fish_config_interactive`). Otherwise every completion script would have to implement that by itself, and consistently to not confuse people.
For completions, that's the right thing to do.
What you want, I'd argue, isn's a completion. What you want is a selection menu. Which could be implemented with fish's completion pager (we have an open issue about making it accessible to the outside), but isn't the same thing.
JSON here is a red herring, since you don't just want to add items (with descriptions), but also control the behavior of the pager ("orientation" and "on_single_result"). That you could do just with arguments to this `select` command (`list_of_items | select --orientation=vertical --on-single=choose`).
yt $URL/somefile
> Would the variable still be expanded? Would an asterisk glob be?No. Pure should mean don't do anything from here on, just pass the arguments to the function.
> If I _did_ want it to expand something even though the function is declared "pure", how would that work?
If expanding should be possible in a pure context, then maybe by using a known syntax like:
yt ($URL)/somefile
Eshell expands Lisp code when using (lisp-code-here) which is kind of elegant. So when in a pure context, you would have to switch to expansion-mode by using curly braces.History expansion `!$` for example, is missing.
SDTIN redirects as I expect `<<<` etc; are also missing. And to get ^R to work again I had to install fzf.
Now, I am fairly certain I'm doing it wrong, but I never found a good place to read how to do it the fish way... If anyone has good replacements for the things I've mentioned I'd be quite keen to hear them.
I've written both my Trello Exporter CLI tool[0] and the first version of my React/Browserify static site generator[1] on it.
I like the scripting syntax too, so much saner than bash.
Anythiing comparable now in fish land?
Is there a way to disable this?
Definitely not for me.
set BROWSER lynx
helpSince I use the non-graphical Emacs (emacs-nox), I can easily start dired-mode in terminal by writing: emacs .
Fish is almost zero configuration and has many useful functions built-in and they are quite fast. Unfortunatelly it doesn't use bash-style syntax like zsh does.
Wait... Is whitespace significant?
I still write scripts in bash for compatibility (when I have to; I try to use Python whenever it's an option), but for my interactive shell, fish is really amazing and I'd hate to go back to anything else.
https://github.com/oilshell/oil/wiki/ExternalResources
Of course I think my own project, Oil, could be a "21st century shell", or I wouldn't be working on it :) It's designed in a principled way, and it's also the only one that's compatible with bash.
I'm not so sold on some of the tokenizing / etc styling, but `cat image.jpg` -> see an image? yes please.
Alternatively, I keep checking in on Xiki periodically, since it has some extremely interesting features: http://xiki.org/
2) list.reverse(macOS, Linux, and the rest of the family)