Fish shell 3.0
github.com
github.com
fish now supports && (like and), || (like or), and ! (like not), for better migration from POSIX-compliant shells
Perhaps the language creators have plans for future syntax that would not allow what you want. Even if for now the language evidently knows what you mean from the error message.
In this specific case, the developers of fish apparently decided && was good to add.
bar = 1 + 1
I do believe Python made the right call, actually: print needed to be unified with functions, and dropping the requirement for parenthesis for function calls would have introduced so much incompatibility as to make the Python 2 -> 3 transition far more difficult.But ignoring the legacy issue, there is a better way to do function calls, and it is ruby.
C style function calls are common, explicit, and consistent. Python adds the best combination of positional, variadic, and named parameters of any language I know. ML style has less punctuation and simpler grouping rules. Ruby has the weaknesses of both.
f 1 2
(f 1 2).thing
Here the parentheses are nothing special, they just hint precedence.As opposed to
f 1, 2
f(1, 2).thing
Awkward special-case commas and parentheses.What does someone want "print (1, 2)" to do if "print" is the built-in function? It prints a tuple in Python 2 and ints in Python 3.
If I say "print 'foo'" though, it recommends "print ('foo')", but doesn't go all the way to assume I actually meant that. There's a piece of code handling that special case that stops short of actually interpreting what I apparently mean.
The frustration around that was the original point.
I take it fish didn't / doesn't support either?
(cd subdirectory && command --that might.fail) && run --command-in-original-dir --only-if-subshell-succeeded
... and expect to still be in the original dir when it's done. Any solution in Fish using "&& cd -" at the end of a block AFTER a maybe-failing command is just wrong, and there seriously isn't any way except saving and restoring $PWD every time you want subshell functionality, or using "; cd -" and manually saving the $status, which is equally as frustrating.There are some non-syntactic suggestions in https://github.com/fish-shell/fish-shell/issues/1439
You were still running the old version, and `echo $version` would have told you.
But maybe now it’s getting closer to production readiness.
I think it's called POSIX.
But calling it "getting closer to production readiness" feels a tad insulting.
It's probably just a bit of snark, so it's not worth getting up in arms about, but it's not really friendly phrasing.
The syntax is different, the semantics are the same.
I find this a really user-friendly way of managing abbreviations I've created.
Have defined the following:
abbr -a gp 'git push'
abbr -a k kubectl
abbr -a n 'npm run'
abbr -a y yarn
abbr -a m make
Now, when I type n, then SPACE, then TAB, I can get the list of npm tasks for example.Fish looks great, and I'm going to give it a try, but I ran into these same old shell scripting issues within 2 minutes of trying to configure my prompt, which is a bummer. Took me 10 minutes (I'm ashamed to say) to end up at `if test -n "$git_branch"` (double quotes are critical).
I'll be excited when someone invents a shell that feels natural in this respect. Perhaps the nature of shells and commands makes this impossible? Or are we just stuck in a box?
The trick is that `test` and even `[` (which is essentially another name for `test`) are ordinary commands, that exist as stand-alone binaries as well as identical-behaving built-ins. They masquerade as syntax, but they're really not. You can even call them from other, non-shell languages:
>>> from subprocess import run
>>> run(('/usr/bin/[', '-n', '', ']'))
CompletedProcess(args=('/usr/bin/[', '-n', '', ']'), returncode=1)
>>> run(('/usr/bin/test', '3', '=', '3'))
CompletedProcess(args=('/usr/bin/test', '3', '=', '3'), returncode=0)
That means that they're not allowed to do anything to variables, because the variables are inside the shell. `test` doesn't see `"$git_branch"`, it only sees the content of the variable.Per the normal rules, `"$git_branch"` is an empty string iff $git_branch is empty or unset. Leaving the quotes off can do unexpected things, though a lot less so in fish. Using single quotes means you get a literal string, without any substitution.
You're just looking for an empty string anyway, so you might as well use the equality operator instead of digging through the 18 (!) unary flags prescribed by POSIX. So I'd write either `[ "$git_branch" = "" ]` or `test "$git_branch" = ""`. Some implementations support `==` as another way to write `=`, but `=` is standard.
This modularity makes the shell simpler, but it's not exactly sane. I think that providing `[` was a mistake, because it doesn't look like a command, but maybe having `test` as a command is better than having special expression syntax just for these cases.
For instance, one way to use it is
test somestring
to test if "somestring" is empty. POSIX mandates that test with exactly one argument succeeds iff that argument is a non-empty string, to allow that. Fish's test is POSIX-conformant.Now, that means that
test -n
is true! Which is also triggered for test -n $var
if $var has 0 elements.This is a problem and should be improved.
If we were to start with a clean slate, could we come up with something natural while still having a clean design with respect to commands? Would it be worth it?
The fact that those details haven't sunk in after more than a decade points to an opportunity for a better design, I think. For example, I can pick up Go, Java, Perl, PHP, JS, Nim, etc and memorize the if-statement details in about 5 minutes. Whether or not anyone would use such a shell, I don't know.
regexes describe this sort of thing quite concisely and IMO a fair bit more readably than e.g. bash's string replacement syntax, which looks like
"${var#"${var%%[![:space:]]*}"}"
when you want to do the equivalent of s/^\s*(.*?)\s*$/\1/In the end I switched back to zsh and enabled "zsh-autosuggestions", which gave me the thing I loved most about fish, without any of the parts that I didn't. There's also some other extensions I'd like to try that enhance history like "McFly" ( https://github.com/cantino/mcfly ), based on this previous zsh-histdb discussion: https://news.ycombinator.com/item?id=15041772
If I just wanted a very simple shell, with good defaults, I'd choose Fish. But much like why I use (Neo)Vim I prefer the DIY approach and have gotten my ZSH to the point over the years (and a hundred scripts later) where I love it.
But ZSH the language is pretty awful and non-intuitive (although way better than bash when you dig into it). Even though I've been writing it for years I still get it mixed up often.
My hope is that Evlish shell will be mature enough soon to use day-to-day, which is the only one I've seen with a modern scripting language (written in Go) and is 10x faster than my ZSH (which is very noticeable): https://elv.sh
As I think you need to know the standard shell language anyway, new shell DSLs are a bit of a turn-off to me. If the language is trivial it's not very powerful and if it's powerful it takes time to learn.
That's why I have high hopes for shells that combine tried-and-true programming languages and their ecosystems to a thin shell layer. Like http://xonsh.org or https://docs.racket-lang.org/rash/index.html .
But if I'm completely honest, I think bash will still be the standard choice in 5 years and weird shells you haven't heard of will still be weird shells you haven't heard of.
Shell scripting finally looks a little saner thanks to fish.
I don't think it helps that it's this huge target area which (the small number of?) users use a slightly different 10% of, and it can be quite complex to integrate new features into existing editor state.
> mpv https://www.youtube.com/watch?v=LBoF1e5YDdQ
fish: No matches for wildcard 'https://www.youtube.com/watch?v=LBoF1e5YDdQ'. See `help expand`.
mpv https://www.youtube.com/watch?v=LBoF1e5YDdQ
^
This, to me, is not user friendly.For reference, pasting the same URL into zsh with the often touted omz (incidentally I recommend you use antigen instead) auto-escapes the URL to
> mpv https://www.youtube.com/watch\?v\=LBoF1e5YDdQTo trigger it, enter a single-quote, then paste the URL, then close the quote once you're done.
We don't attempt to detect URLs because that can go wrong, and because we don't know your intention. So we use the fact that you're inside single-quotes as the signifier that we should escape things - which also allows this to be used for e.g. pasting shellscript as a literal argument.
Here is a demonstration about what I'm talking about: https://i.imgur.com/d64lwaB.mp4
> "We don't attempt to detect URLs because that can go wrong, and because we don't know your intention."
That much I've gathered from seeing this issue raised with fish people in the past, and in my humble opinion it's the wrong approach. In this mentality fish is forgetting it's stated goal of being user friendly which should in some cases, mean making sane educated guesses about the user's intent. How often do users of the fish shell paste a string that starts with 'http' and contains a '?' that is meant to be a wildcard? Not very often at all, if ever; fish is now evidently planning on deprecating that ? wildcard so I think the fish devs agree!
The way I see it traditional shells aren't user friendly because they take a conservative approach, doing crap like not enabling fancy autocompletion features out of the box because "what if the user is running on a timesharing PDP-11 and can't spare the CPU cycles?" Breaking from that mental trap is bold and should be commended, but in the case of not escaping pastes I think fish has fallen into that conservative mindset again. zsh/OMZ does it by default and I've never heard of anybody getting screwed over because of it. Fish could and should do it by default too.
Okay. Now you can do it for http. What about ftp? Or "file://"? Do we detect _all_ kinds of URLs?
And even if we do, and we don't miss some or see something as a URL that isn't, what about _other_ kinds of things? What about shellscript? E.g. if I want to just execute a bash one-liner from the internet, I can do
`bash -c '`
and paste it and it'll work out. That won't be detected as a URL, because it isn't one.And I've seen a case of a URL with variables just yesterday (though I admit that's a coincidence):
https://docs.travis-ci.com/user/deployment/custom/ has "sftp://${SFTP_USER}:${SFTP_PASSWORD}@example.com/directory/filename". How do I paste that with the zsh thing? Do I need to go back and edit all of that?
So, instead, we have one rule: If you paste it insde single-quotes, it'll be escaped. If you paste it outside, it'll be inserted as-is (but still not executed). No matter what the contents, no matter where.
Some is better than none, that's the lesson learned from OMZ's implementation of this feature. It doesn't cover every single scenario, but it covers enough to make people's lives easier in the common cases. That is the sort of bold decision making that the fish developers should be doing.
`bash -c '`
sftp://${SFTP_USER}:${SFTP_PASSWORD}@example.com/directory/filename
Both of these get pasted unchanged in zsh. I suggest you try it out. But more to your point, this would get improperly escaped: http://${SFTP_USER}:${SFTP_PASSWORD}@example.com/directory/filename
And you know what? That's fine. Because whether you choose to auto-escape or not, something will break. The question you and the fish developers should be asking is which is more likely? Auto-escaping works the vast majority of the time and only fails some of the time. And when it fail in the rare case, the UX is no worse than when it fails in the common case.>"If you paste it insde single-quotes, it'll be escaped."
First of all, that's not what I see. Maybe you're talking about bracketed paste mode or something that's invisible to the user, but when I paste a URL containing wildcards into single quotes in fish, I don't see any escaping taking place. I assume single quotes are the "verbatim quotes", e.g. `ls '*.txt'` doesn't show you all txt files because that wildcard isn't expanded inside those single quotes. With that in mind, it's not clear to me why a string pasted into single quotes should be escaped, it doesn't need escaping and I don't see any escaping being done when I try it out myself.
There's nothing magic about making `mpv 'https://www.youtube.com/watch?v=LBoF1e5YDdQ'` work. Dumb old bash accepts that too.
I disagree. I'd rather have predictability here.
>when I paste a URL containing wildcards into single quotes in fish, I don't see any escaping taking place.
That's because you've not pasted anything that needs escaping. In single-quotes, only single-quotes and backslashes are special, and they are escaped.
Even if you paste script that itself contains single-quotes, it'll work.
Yes, and you've not solved that.
You've only solved that one special edge-case where they're pasting "a youtube url".
This only pushes the confusing bit further along, and adds some more cruft on top!
Quoting is a fact in shells. The way these languages work is through loads of string interpolation, so you kinda need to learn that. And I'd prefer to make the rules there simple than add special cases for URLs.
But yeah, I have had a ton of issues where commands fail because I didn't quote URLs, and in the beginning it wasn't as obvious as it is now. I don't have a solution, just saying that this does add friction for newcomers.
If the URL contained a space that is not URL encoded, that would be user's fault.
wget http://example.com/files/(basename -s .jpg *.jpg).png
but ( is a legal character in urls, so fish can't possibly predict which use is desired.The way we plan to address this in fish is to make ? an ordinary character instead of a wildcard. Then URLs will have nothing to escape.
You can opt into this change today by setting the 'qmark-noglob' feature flag:
set -U fish_features qmark-noglob
now ? no longer globs.Unfortunately, `&` is used in URLs, and is special also in the middle of a token.
Sure that's true, but on the other hand I don't see anybody ever recommend default zsh. It seems pretty widely understood in the community that OMZ (or their equivalents, I have unrelated complaints with OMZ) are sane 'defaults'.
I'm not sure how to feel about only now learning its indexes start at a [1] though (╯°□°)╯︵ ┻━┻
Love it otherwise.
Starting at [0] is not objectively better than [1]. I actually believe it is objectively worse.
When teaching people to program, portraying the idea of “jumping zero steps” into a list to get the first element has been an inefficient embarassment every single time I’ve been present.
Re. Dijkstra’s argument:
1: Everybody has an intuition of an ends-inclusive “from x to y”. Everyone would include 2 and 12 if asked to “count from 2 to 12”.
2: What’s wrong with using three dots? What is pernicious about them?
3: Labeling and length calculations always require a shift of one. If we make the ends compatible with thought-free length calculation, then sequence labeling is always off. If we have intuitive sequence labeling then we need to adjust either end label by one to get the length. The latter approach is far more intuitive. Also, one should just ask the damn computer what the length is. That’s what it’s for. Pardon the language. Especially as we will always want to leave behind and tend to leave behind the abject simplicity of hand-calculating the length of our data from the labels of the bump stops; Our data structures may not even support that calculation. So why couple ourselves to that calculation and sacrifice the ability to have sequence labels make sense?
And why don’t we just have both? Keep array[0] as first, fine, whatever, and add array|1| as first too.
This doesn't seem to be much of an argument so I'm not even sure what to address.
0-based indexing makes sense to me in C where arrays are just pointers and the index is an offset, and doing pointer math is a regular part of the programming experience. But most languages have come a long way from that, and collections such as arrays are much closer to a natural metaphor (a list of things). As such, natural ranges (inclusive) seem more appropriate to me.
julia> x = collect(1:15)'
1×15 LinearAlgebra.Adjoint{Int64,Array{Int64,1}}:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
julia> x[1:10]'
1×10 LinearAlgebra.Adjoint{Int64,Array{Int64,1}}:
1 2 3 4 5 6 7 8 9 10
julia> x[11:end]'
1×5 LinearAlgebra.Adjoint{Int64,Array{Int64,1}}:
11 12 13 14 15
julia> x[11:length(x)]' # alternatively ...
1×5 LinearAlgebra.Adjoint{Int64,Array{Int64,1}}:
11 12 13 14 15The best of both worlds is when a language provides both end-exclusive and end-inclusive slice syntax, as in e.g. Nim: x[0..<10] is end-exclusive, and it's very clear that it is.
Half-open intervals are appropriate when you're thinking of them as subspaces of a continuum. When you're thinking of them as subsequences of discrete elements, closed intervals are usually more natural.
Ruby is the only language I know that really seems to have tried to address the problem, providing different syntax for every way you might want to take a subsequence from an array:
x[5..8] # elements [5] through [8], including [8]
x[5...9] # elements [5] through [9], excluding [9]
x[5, 4] # four elements, starting at [5]
If you were working in pure, pencil-and-paper mathematics, you'd choose whether to index a particular sequence from 0 or 1 based on what made your formula look nicer. Both are common. But that's not an approach I'd suggest for a programming language.IMO, it's better to have dedicated syntax for index-from-end, as part of the range syntax. Again, Nim does that: x[0..^1] (although unfortunately they didn't make it symmetric).
This is true, and note that my suggestion requires it -- the only way to distinguish x[-0] from x[0] is to have the parser do it; -0 and 0 are the same number.
sudo apt-add-repository ppa:fish-shell/release-3
sudo apt-get update
sudo apt-get install fish
Despite all the disclaimers (or perhaps because of them), the AUR is IMHO one of the more safe and well-thought-out ways of including third-party stuff. It's relatively simple to verify that the package is installing what it says it is, because you can check the PKGBUILD file, which is short and simple to read. I would have no idea how to go about verifying that a .deb file was what it said it was.
My instructions I have for installing yay are:
Go to https://github.com/Jguer/yay/releases Download the yay release and extract it, a clone won't have the build files, download the release. cd to the extracted copy ./yay yay (install the AUR copy of yay using your local copy of yay)
Then `yay -S <package-name>` and done, it grabs the repo if it has to and runs the build steps to build from source. It is the most frictionless package management experience I have every experienced on Linux.
And I have used `apt-get` on Ubuntu for many (10?) years (go find the PPA yourself dammit) and also `yum` RHEL7 for the past year. Fedora and Debian need something like this too.
Installing an AUR package without a helper (which you have to do to install a helper in the first place) basically amounts to:
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si
But then you would have to manually check for updates etc, so the helpers are pretty useful.* piping to FIFO files, but it throws an error if you pump too much data into them
* waiting until one process finishes before starting another. That's not a correct implementation either
Bash apparently resorts to black-magic.
https://www.gnu.org/software/bash/manual/html_node/Process-S...
Same here. Fish’s configuration defaults are very well thought out and incredibly smooth. My config.fish is only nine lines.
python -m http.server... and some additional things that you can't do in zsh without plugins, such as autosuggestions[0].
I can see how its easier to just make sure Fish is installed, but in some situations (NFS home directory mount and zsh already available) it is easier to install oh my zsh in HOME.
You can use `prevd` and `nextd`, or you can use key bindings, like the default alt-left and alt-right bindings.
$ dirs -v
0 ~/baz
1 ~
2 ~/bar
3 ~/foo
4 ~/spam
Then "pushd +3" will take you to ~/foo. This hardly seems "opaque".I mostly like that it tells me when my commands are wrong or when it autocompletes (usually hints are based on the directory you're in which is nice).
I tried zsh ages back and if I remember correctly you had to configure it yourself to get to such a point.
Note I only install fish in systems I fully own, I rather not confuse coworkers by using another shell. Also forces me to be more careful when in a production system.
Also, inline function creation, editing and saving are also pretty killer. I make way more one-day use functions than on bash with fish.
> Finally, a commandline shell for the 90s
It makes me wonder, how would a shell for 2020 look like?
This surprised me, but maybe it's for the best. It got in my way more often than I used it. I'll still miss it.
- It keeps the state separate from the main shell, including $PWD (so the `cd` doesn't affect the main execution "thread")
- It executes some script in the background
Fish's backgrounding is currently limited to single external commands (not even functions). That's https://github.com/fish-shell/fish-shell/issues/238.
It also doesn't have the capability to keep $PWD separate, though other variables can of course be declared locally. That's https://github.com/fish-shell/fish-shell/issues/1439.
The workaround is, if you actually need this, to run it inside `fish -c`. Which is essentially what this syntax is doing in the background anyway (it needs to fork off because `chdir` applies to the entire process).
(Disclosure: I'm a fish developer)
If you're doing something like this in the background, then that usually means it takes a "long" time (or you'd just wait for it). So the overhead of starting an additional shell simply isn't important.
Not that it's always irrelevant, but that's why the issues are still open.
But I do often do something like:
( cd somewhere ; do_something )
just so I can avoid having to do
pushd somewhere ; do_something ; popd
(or the equivalent).
for ((i=0;i<4;++i)); do (./ad-hoc-operation >out-$i.log 2>&1 ) & done
Can be anything from image processing, video transcoding, file decompression, file system analysis - anything I think may complete faster with some concurrency.seq 0 3 | parallel "./ad-hoc-operation > out-{}.log"
Also, bash and xargs are available everywhere, on production servers as well as dev laptops. Anything that requires installing something has a big hurdle to overcome - this counts for fish too.
I've been using fish for years and it hasn't been an issue. You can keep bash installed for any hardcore scripting, and just fish as your interactive shell.
I use Cmder / Conemu now but it doesn't seem to have even 10% of the features of a fully fledged shell like this.
I've not used it myself, but I know that fish is available in WSL and cygwin (though this release had some issues there).
Interesting about it working on cygwin / WSL. Maybe I'll give that a go. I'm sure someone's also made Powershell enhancements that add similar functionality too.
Frequency illusion (or Baader–Meinhof effect): The illusion in which a word, a name, or other thing that has recently come to one's attention suddenly seems to appear with improbable frequency shortly afterwards.
The down side is that it's functionally very limited compared to zsh. fish's completion functions are easy to maintain, but the trade-off is that they don't offer the fine-grained contextual control over behaviour that zsh's do. Features like globbing, sub-shells, job control, co-processes, modules, &c., are absent or extremely limited. There are fewer knobs and switches, though as mentioned it tries to do the right thing for most people by default. And a consequence of the syntax being clearer is that it's also much more verbose, which i guess you may or may not appreciate.
Over-all i think fish is a good shell for the (very common) type of person who doesn't really care about shells but was drawn to zsh just because it has nicer completion than bash and lets you write fancy prompts. I don't think it's a good fit, yet, for people who write complex scripts or regularly make use of stuff like extended globs, background jobs, and fancy parameter expansions.
It's like fish can read your mind.
Some scripting example : https://nicolas-van.github.io/programming-with-fish-shell
(I use zsh as shell, as enough customization can do much of what fish can achieve with bit more stability but maybe they fixed with 3.0 which I haven't tried yet.)
Fish "just works", and you won't have any lag. It is a delightful beginner experience, too.
A Zsh veteran could probably replicate parts of the Fish experience in Zsh, but ultimately they are different tools. I use Fish at home for casual shell stuff, but I write shell scripts in Zsh, and I use Zsh at work whenever possible.
Functions should be on fpath, zcompile'd, and loaded with autoload. Otherwise you have to read and parse 100s of lines of code every shell invocation... yikes.
I think it had to do bass not working though, so the plugin didn't work. Now that I'm on NixOS I might give it another shot.
fisher add FabioAntunes/fish-nvm
worked for me!one thing I have noticed is, some packages (I can't remember which, perhaps it was virtualenvwrapper?) come with tools which work with bash or zsh. I have rarely seen fish being mentioned.
edit: it was gcloud sdk and it added command line completions:
# The next line enables shell command completion for gcloud.
if [ -f /Users/avi/code/google-cloud-sdk/completion.zsh.inc ]; then
source '/Users/avi/code/google-cloud-sdk/completion.zsh.inc'
fitime fish -c '[function]'
but that includes the time to launch fish.
This, of course, wouldn't matter for any reasonable usecase but I like a fancy prompt and don't like waiting for it to run.
In fact, I customized my prompt to pretty print the previous command's duration if it took longer than 3 seconds or something like that.
You can still have bash installed and anything that requires significant scripting could probably be done easier with a language for scripting. I want my shell for my terminal. This is also the reason I don't like the verbosity of Powershell, however much an OOP paradigm better supports scripting.
It's a widely-touted meme that scripting is more easily done in a general-purpose language than in a shell, but this is mostly true of the (common!) category of tasks which use the shell to launch sed &c. to perform string processing which is built-in in general-purpose languages. It's comparatively hard (i.e. verbose and error-prone) to replace a shell language with a general-purpose language in its core competence, viz. launching processes, muxing I/O, and coordinating parallel processing.