Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I remember switching from bash to zsh a few years back and thinking I was the bees knees. After the switch trying other shells seemed like bike-shedding because, I mean, what more could a shell? Then I got a new computer and decided to start from scratch with my tooling and downloaded fish. I was shocked how it instantly made zsh feel cumbersome and ancient.

Heartily recommend others give it a try as a daily driver for a couple of weeks. I liken it to Sublime Text: an excellent “out of the box” tool. Just the right amount of features, with the option to add more if you want. But you also don’t feel like your missing out if you keep it bare bones. A great tool in and of itself.



Same here. I used it for about 3 days before I installed it on all my systems and permanently switched. For me, it was like the first time I learned a non-Latin language, and my eyes were opened to how much stuff I took for granted was completely arbitrary.

For example, here's how you write an autoloaded function "foo" in Fish: you make a file called "foo.fish" in its config directory. Inside that, you write "function foo ..." to implement it. There's no step 3. That's it.

Want to customize your shell prompt? Follow the process above to write a function called "fish_prompt" that uses normal scripting things like echo, pwd, git, or whatever to write your prompt to the screen. There's no step 2. That's it.

Fish was revelatory. Other shells of the same vintage feel hopelessly outdated to me now. For context, I was the maintainer of FreeBSD's "bash-completion" port for a few years way back when. It's not that I don't have experience with other shells. I have plenty. I just don't want to use any of the others now.


This was more convincing to me than the GP comment, especially the shell prompt part.


Is the “foo.fish” name required? Could I have “bar.fish” with “function foo…” inside and still autoload function foo?


Not autoload, no. You can have as many functions as you want in a single .fish file, but it'll only be lazily autoloaded if it has the same name as the command you are trying to execute. It's how we avoid doing the I/O of scanning all fish directories and parsing their contents at startup.


...and you can still explicitly source the files if you want to load the functions elsewhere.


Interesting, I went the other way about 7 years ago - switched from fish to zsh (initially with oh-my-zsh). The interactive experience was similar enough on both shells, and the performance was great on fish and okay-ish on zsh, but two things won me over:

1. With zsh, I can copy-paste some bash snippet and in 99% of cases it will just work. Aside of copy-pasting from StackExchange, I also know a lot of bash syntax by heart by now, and can write some clever one-liners. With zsh, I didn't need to learn everything from scratch. (I guess this matters less now that you can ask AI to convert a bash one-liner into fish one-liner?)

2. For standalone scripts... well, I think it's best to reach for a proper programming language (e.g. Python) instead of any shell language, but if I had to use one, I would pick bash. Sure, it has many footguns, but I know them pretty well. And fish language is also not ideal - e.g. IIRC it doesn't have an equivalent of `set -e`, you have to add `; or return 1` to each line.


I use fish and on the very, very rare occasion I need to copy and paste bash from the internet it's pretty easy to just type 'bash' into fish and paste it in. Its not like bash and fish conflict, you can have them both installed.


FWIW, fish is much more bash-compatible these days. We've introduced support for a lot of bash-isms that don't completely break the fish spirit or clash with its syntax in the last few releases.


I personally liked "; and" but... "&&" solves around half of the problems with copy-pasting and does not look terrible, so it was probably the right thing to add.


Thanks! I'm trying fish once in a while, and currently, the following are missing:

    $ cat <(echo "Hello")
    Hello
    $ cat <<<"Test"
    Test


In regards to the former, you use the psub function instead of special syntax (a pattern that fish often follows):

$ cat (echo Hello | psub)


Also, fish doesn't have here-strings/here-docs, I believe. That is far and away the main thing I miss in fish.


> 2. For standalone scripts... well, I think it's best to reach for a proper programming language (e.g. Python) instead of any shell language, but if I had to use one, I would pick bash. Sure, it has many footguns, but I know them pretty well. And fish language is also not ideal - e.g. IIRC it doesn't have an equivalent of `set -e`, you have to add `; or return 1` to each line.

I'm sure you know this, but: no particular reason the interactive shell you use has to match the shell you use for scripts. All of my scripts are in bash, but I haven't used bash interactively in decades now, at least on purpose.


I write all my scripts with the hash bang as "#! /bin/bash" so even though fish is my interactive shell, I still use bash for all shell scripts. I think the restrictions you mention only apply if you use "#! /bin/sh" rather than bash specifically.


Just fyi, you should use `#!/usr/bin/env bash` instead of `#!/bin/bash` or whatever because you can't assume the location of bash (but the location of `env` is indeed portably fixed). e.g. FreeBSD (and macOS?) has bash at `/usr/local/bin/bash`


And NixOS has bash somewhere in the Nix store... :)

Clarification: /usr/bin/env should be used for pretty much every shebang since it looks up the binary on $PATH.


That assumes you care about portability. Not everybody does.

Writing portable software is difficult, and doing it for shell scripts even more so. Blindly pursuing portability for its own sake is not worth it. Weigh the cost of portability against the odds that the software will ever run on different systems.

For me personally it is never worth it to write my personal programs portably. This would require that I test them on different systems that I do not even use. Pointless.


It’s not so much a portability thing IMO as it is a utility thing. If I have a newer bash in my PATH than what is in /bin/bash, I want to use it.


bash is /bin/bash on macOS, unless the user really likes bash, in which case it's probably /opt/homebrew/bin/bash or /opt/local/bin/bash


I wouldn't say I particularly like bash, bash has seen a ton of improvements since Apple stopped updating the vendored version. Using that old bash which is frozen for non-technical reasons just seems stupid to me.

If you don't want bash-specific features, you might as well use zsh or dash or whatever lives in /bin/sh. If you do want bash-specific features, you might as well take advantage of the latest and greatest.

On that note, on my Macs, the bash I want is usually /opt/pkg/bin/bash or /run/current-system/sw/bin/bash :)


In any of those cases, using `/usr/bin/env bash` gets what the user probably wants


Yeah I'm just commenting on what the path for that would be


I'm confirming. Often, when you run a script on more than just your own computer, bash is located in unexpected places.

For me, for example: `/data/data/com.termux/files/usr/bin/bash`

In such cases, scripts containing the absolute path to bash in shebang do not run correctly.


I “devolved” mostly along the same path. Bespoke shell to OMZSH to Zsh to Bash.

Zsh has a few nasty Bashism footgun incompatibilities. If I remember correctly the worst one is with how globbing / “*” works, which is why that is guarded with an option.

My main reason for sticking with Bash is that it’s everywhere, and the places where it isn’t try very hard to support the most-used featureset of Bash.

A stock Bash shell does feel a little naked without my dotfiles though :)


Bash on osx is pretty old due to avoiding GPLv3. I think they have zsh as the default login shell


True. But it’s easy to install Bash 5 via Homebrew or MacPorts.


Reading the associated issue (https://github.com/fish-shell/fish-shell/issues/510) about the lack of "set -e" was interesting as it highlighted how weird Bash, and shell scripting in general, is from a programming language perspective. Imagine programming in any other environment where every function you call could either succeed or fail catastrophically. There's some talk about adding exception handling to Fish, but maybe the sensible thing to do is to have a mode where Fish ensures that you've dealt with each possible error before moving on. Which is what you would do anyway if you were invoking external programs from a non-shell language (like Python's subprocess.check_call).

In any case the discussion in that issue made a convincing (to me) argument that if you're doing the sort of scripting for which "set -e" makes sense, which is most of it, you should be using Bash. That doesn't mean you need to use Bash interactively though, as others have pointed out.


> Imagine programming in any other environment where every function you call could either succeed or fail catastrophically

There's not much to imagine since that's pretty much every other language?

Sure you can recover with error handlers (sometimes[0]), but by default all of them will hard abort in case of exceptions.

In our modern language landscape shells are very much the odd ones, where errors are completely silent by default and the thing just carries on oblivious that the world around it might be crumbling completely.

[0]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-...


> Imagine programming in any other environment where every function you call could either succeed or fail catastrophically

Laughs in client-side JS.


Hmm? It’s not like a JavaScript exception crashes the entire browser tab.

Client-side JS is event-driven. An unhandled exception stops processing for that event, but doesn’t block other events.


But, scripting languages are not programming languages, scripting languages are made to run commands, and by default a script should halt if a command fails, at least in the CLI execution context. The problem is, scripting languages mix programming context and scripting context, so a condition written in the script shouldn't be treated as a CLI exit status. Anyway, I don't use fish for scripts just for the lack of exit on command error. That's essential while scripting.


I think that oilshell is aimed at people like you. I’ve never used it, but their website does make some interesting points about how a shell ought to work and how this could be compatible with bash.


As a go programmer, "; or return" makes a lot of sense to me


I went bash -> fish -> zsh.

The main reason I switched is because zsh can (often) source bash scripts and can use bash completion scripts (usually), and I was tired of having to translate things from bash to fish. I also ran into a few things where something that was relatively easy to do in bash was impossible to do with fish. But that was years ago so maybe that is less of an issue now, and I don't remember exactly what it was.

Having used zsh, I think a big advantage it has over fish is the completions. There are completions available for more programs for zsh, and the zsh completions are sometimes higher quality in zsh.

But I do generally like the syntax, and good out of the box experience of fish. I wish it had a bash or even posix compatibility mode and more available completions.


I can relate with your comment a few years ago, but later the situation drastically got better, while not perfect yet (i.e. I still need a custom autocomplete function for aws). You might want to give it a try now anyway.


I used bash for ages, and never really saw what zsh offered in comparison: I would have had to customize it almost as much as bash, and it didn’t really give me anything new.

Fish was so much better than either out of the box, and I still have done virtually no configuration other than setting it up to use my common starship prompt, which is supported in bash as well.

I don’t understand personally the argument about not having bash syntax. If I want it, I just run `bash`.


> I don’t understand personally the argument about not having bash syntax.

Three main reasons:

1. The Fish language is only useful only for somehow extend fish itself, so it is pointless to spend time learning and practicing it unless I'm writing something for me or other Fish users.

2. Sometimes we need to copy and paste something to our shell. When using Fish I must remember to set variables with set, get the status code with $status instead of $?, use () instead of $() and so on, which is a unnecessary overhead

3. Bash's syntax is a hell: sometimes we forget a space, an escape, use end or done when we need to use fi or esac and so on. I don't trust my Bash code, I type everything in the terminal to check if everything is ok. In Fish I just can't do that...

> If I want it, I just run `bash`

That's what I do for 2 and 3. But when I do that I don't have the nice features of Fish...

I still love Fish, though.


Also, do consider xonsh.[1]

It's a Bash-like shell written in Python. It has significant overlap with the awesomeness of fish, and has the advantage of being able to write your shell scripts in a Python dialect. So if you know Python, the mental burden is much lower.

On top of that, it's cross platform, since Python is. No WSL needed.

I switched to it in 2018 and haven't looked back. Originally it was just because I wanted a better command prompt environment in Windows for work, but I liked it so much I switched to it in Linux as well.

(And yes, you can type any Python statement right in the command prompt).

[1] https://xon.sh/


I know it's a typo but this:

> what more could a shell?

Is quite good. It could almost be the tag line for fish shell.


Do you mind sharing what you think are the killer features of fish?


Fish has a lot of features out of the box I find really useful:

* Command auto suggestions as you type based on your history

* History search (using up arrow) based on a partial command

* Helpful completions and descriptions when you hit TAB

* Muti-line command editing

* Syntax highlighting

You can get all those same features in Zsh by using plugins, but those features work out-of-the-box with Fish with zero configuration. Zsh is a bit of a pain to configure, and pretty anemic without plugins. Fish makes configuration optional because it works how you'd hope your shell would out of the box. Even though Zsh has those features as plugins, they're kinda janky, not well maintained, and often conflict with other plugins.

Additionally, Fish also has:

* Excellent built in commands (string, math, argparse)

* Sane scripting (word parsing where you don't need to quote everything, etc)

* Great documentation

* A web-based configuration if you're into that sort of thing (it's a bit of a gimmick for beginners)

The main reason I use Zsh (or Bash) at all is for POSIX/portability, or for when I can't install something else. But for an interactive shell on a machine I control, it's hard to compete with Fish for speed, features, and ease of use.


Other two things: Fish has an amazing integration with Docker and Git. If you type:

docker stop <TAB>

it suggests the hashes of the containers. About Git, you can, for example, type:

git checkout <TAB>

and Fish will suggest the available commits and tags. If you start to type a string it will also suggest the hash of commits whose messages match the string.

I know that zsh may do the same using plugins. But Fish have all of that by default without being bloated. I use Fish since 2018, never installed a plugin and never thought that something was extra in it


For me, it's that the ergonomics are straightforward, and everything works out of the box. If I find myself on a new machine, just installing fish gives me an ergonomic setup without having to install too many additional tools or mess with configuration.


Also, fish_config is there if you want to make quick changes without having to look up syntax.


Being able to avoid OMZ and entire cargo cult of zsh configuration performance “hacks” that litter the internet.

Really, not needing to pull in other people’s janky scripts because the built-in features work well is huge. I still configure fish and use a few scripts, but it’s the lack of the massive cottage industry that is the primary draw for me.

Of course, many devs see that as a failing: “how could a shell do its job well without a thousand knobs to tweak?”


We had the exact same experience, still in love with fish!


My only issue with Fish is when pasting things from the web that assume Bash, a lot of the time it just works, then now and then I get screwed. I don't know nearly enough Fish or Bash to switch. Still though, I prefer Fish ultimately.


It’s interesting how many folks in the comments have essentially this complaint, of not being able to paste bash from the internet. I just run `bash`, paste the thing, and then exit bash.


People don't realize they don't have to stick to a single shell for both scripting and terminal use.

I use zsh with plugins which pretty much makes it act like fish's convenience but one can use fish as their shell scripting while keeping the "bash" compatibility by keep using zsh or bash under terminal.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: