When you open a new shell, by default it'll say something like `user@hostname: cursor`. Starship lets you change that to whatever you want, but out of the box it hides unnecessary information, and shows you stuff like the your language and package versions, if you're navigated into a git repo, for example. Take a look at it yourself, there's a lot you can do
Yeah. I love the default starship and install it on every shell I use, currently PowerShell + fish + bash. It shows me all kinds of useful info like which k8s cluster I'm logged into, which AWS/Azure/GCP account, whether the current directory has uncommitted git changes...
This is true. However I was calling out, wrt fish, the only two additional "plugin" type things I add in order to have a comfortable shell environment.
Fish conveniently provides history based autocomplete so I don't need to setup some plugin for that.
I used fish for a while (and really enjoyed it, it's a great shell), but it's formatting is too inconsistent with bash for me. I found myself dropping back to zsh so often to write documentation or run a playbook that I switched back.
If you often share shell code with others it may be to big of a leap for you.
If you're running a script, you can use edc/bass to run it from a fish shell. Fisher is one of the first things I install after fish, then bass, z and a few other helper plugins.
I use fish since they relented and allowed to use && and || instead of ;or and ;and. Still, I always script with bash (or POSIX sh for certain things). I can't recommend that split enough. Also, I can't speak highly enough of shellcheck[1], it's a lifesaver especially when you target POSIX.
Tried fish a couple of times, it's really good, many zsh plugins are created to replicate features of fish. But it's not my default shell, because I need to run bash shell scripts and I didn't find any way to do it in fish. So I am stuck with my slow zsh.
I asked an LLM to translate a bash script to fish once and it just changed the shebang line... cracked me up.
Edited to add:
There's a program out there for translating bash to fish called babelfish. It's pretty good but not complete, so it won't work for all scripts. Worth checking out, though:
I often see people complain about this but I seriously don't understand. The shebang at the top of a bash script will point to bash no matter what your shell is.
Yeah I would have thought so too, but I have personally ran into this issue running fish-shell on my mac. There are some scripts I need to run that do not work even when I shebang the script to run in bash.
Yes, it really is odd, I couldn't figure it out so switched back to zsh.
The specific issue was to do with a script we had to run to gain ssh access to an internal network. I can't post the script, but it was related to openssh.
Even logging into bash and running the script didn't work, I had to remove all fish binaries, symlinks, etc and set my shell to bash/zsh. Maybe it was an issue with how I installed fish, but yeah like I said, very strange, but I can vouch that I've encountered a similar issue before
It's a real shame because I think the fish syntax alone is worth the switch, for my personal machine(s) I use it.
Edit: this was years ago now, about 4 years, maybe the issue doesn't exist anymore but it caused me a bit of grief at the time (beacuse I didn't know it was related to using fish!), I've not since tried it again. I might try again and get back to this thread
fish doesn't have builtins but all fish commands are external binaries or fish functions. i think at one point early in the history of fish those binaries may have been installed on the global path so they were accessible outside of fish and possibly there was a name conflict with one of them that this script triggered.
although such a name conflict should not have happened (and i can't think of which command such a script might have used that would also be a fish command), and the global path thing was also fixed soonish. but this is all a faint memory, so i am not sure i remember any of that right.
- cd
- source, .
- eval
- string
- and
- or
- builtin
- command
and many others.
Not sure what the point of distinguishing between fish builtins and fish functions is; whether a builtin is shipped as a function distributed with fish or a reserved word in the fish evaluator seems like an implementation detail.
fish has grown and evolved. i was referring to this:
Builtin commands should only be created when it cannot be avoided. echo, kill, printf and time are among the commands that fish does not implement internally since they can be provided as external commands. Several other commands that are commonly implemented as builtins and can not be implemented as external commands, including type, vared, pushd and popd are implemented as shellscript functions in fish.
if i remember correctly, this led to some useful commands that are builtin elsewhere to be external binaries shipped with fish. but since those where not actually tied to the fish shell they could run without it, and if they ended up on the global path be accessible from other shells.
the relevant text on the website has been changed, but it is referenced here:
If you wanna add a builtin like that to your own distribution of fish, you could do it cleanly by keeping those binaries in /usr/lib or /usr/libexec and then wrapping them in a fish function that ships in fish's install prefix. (This is basically how fish's Python scripts for generating completions from manpages are shipped today.)
Most likely a command in your script (or one source-d into it) makes an assumption about your $SHELL or login shell that is true for bash/zsh but not for fish.
See my other comment and let me know if that script contains
ssh-agent $SHELL ...
or
exec $SHELL ...
or
eval "$SHELL ..."
or similar. :)
If it ends up passing a script file or arguments to $SHELL and you wanna fix it without rewriting it, just add some conditionals where $SHELL is used and use the equivalent flags or syntax when $SHELL indicates fish.
Alternatively, if you just wanna work around it, just launch that script like
This is plausible if you have scripts that do dumb shit like
eval "$SHELL ..."
because (at least sometimes?) $SHELL is set by your login program and not your shell, and dropping to an alternative shell by just typing
zsh
and hitting enter or whatever won't reset $SHELL.
You mention elsewhere that this had to do with a special OpenSSH setup, which also fits.
One of the things you can do with ssh-agent is use it to launch a child process with a dedicated SSH agent that (only) has certain keys on it, and which exits once its child exits. This is sometimes handy for deployment scripts or doing git checkouts or whatever because you can ensure you don't get locked out for too many auth attempts because the user just has too many extraneous keys on the agent associated with their normal user session.
If you were leveraging this feature for interactive shells, you might be tempted to use $SHELL to decide what executable to have ssh-agent launch, so that you could (for example) accommodate both bash and zsh users and let them launch a shell with a special SSH agent but which honors their usual preferences by loading their usual shell and reading their usual zshenv or bashrc or whatever it is.
You mention as well that you had to actually uninstall fish to get things to work again, and also that you were on macOS. I can't be certain about the cause here, but one obvious thing occurs to me:
macOS doesn't handle environment setup like any 'normal' (Linux or *BSD) Unix. On normal Unices, your login shell actually also launches your graphical user session, so to configure a session-wide environment variable, you just set it in your shell startup somewhere. On macOS, login shells are actually only used in SSH sessions and terminal emulators. If you want to set environment variables for apps that are not launched from terminal emulators, you have to use hacks (like Doom Emacs' env file, for example) or configure them for the whole user session via a LaunchAgent.
In the case of $SHELL, that environment variable is used by editors to determine what shell to use when you run external commands. Without it, if you are a fish user and you launch MacVim or Emacs (or, presumably, VSCode or anything else) from your dock, when you go to launch external commands, they will run not in fish but some other shell (probably zsh or bash). If your install method for fish tried to handle this for you (pkgsrc and Nix don't, but the .pkg from the developers or Homebrew might, idk), or if you dropped to bash only from terminal emulator windows that were already running fish instead of opening a new session for bash, you may have wound up in a bash session where $SHELL was still set to fish. That's the only reason I can think of for why you might have had to actually uninstall fish to get this ill-behaved script to work right.
Anyway:
1. As a user, pay attention to what $SHELL is.
2. As a script author, never use `eval "$SHELL ..." or equivalent`.
> macOS doesn't handle environment setup like any 'normal' (Linux or *BSD) Unix. On normal Unices, your login shell actually also launches your graphical user session, so to configure a session-wide environment variable, you just set it in your shell startup somewhere.
That's no longer true for Linux, at least. GDM launches stuff via systemd user sessions, which do not source your profile.
I'm not sure how I feel about it. The old way feels 'simple' to me, but the new way does mean that shell misconfiguration won't hose your GUI sessions.
At various jobs I've often seen bash scripts to set up your environment (mostly environment variables). Running a bash script with shebang will not preserve the changes to the environment variables, will it? At least that was my experience.
I use xonsh and fortunately it has source-bash for stuff like that.
You don't have to translate them. Just stick `#!/usr/bin/env bash` on top. It is totally fine to use fish as your "terminal shell" and default to Bash for scripting for things like automation, etc. I've been doing this for a decade. I will also add that Fish is so good by default that I barely have any config.
Is the issue running bash shell scripts or copy-pasting one liners? If the latter, you can always use the command flag for bash to execute one liners within its own context.
bash -c "grep word file.txt | sort -u | tail"
Scripts shouldn't be an issue as long as you either launch it with the proper shell or configure the shebang correctly after making the script executable.
What do you mean? If the script is using hashbang, you can just run it. If not, run "bash script.sh". If you need to source it, use the plugin "bass". If you want to run a one-time bash command copypasted from somewhere, just type "bash", run your command, and then ctrl-c.
I've used fish for about 5 years now, and I don't really wanna go back. Out of the box, without any addons at all, it does everything my old zsh setups did, with a package manager and plugins. The only change I've made to it was spacefish (now starship.rs)
You may also want to try z, and, err... I don't have my config with me, but I have a plugin that notifies me if a long-running background command finishes, which is really handy.
While there is a tool called `babelfish` that will automatically convert bash to fish, in practice it's rarely needed.
You can run bash scripts from fish.
For sourcing, often it makes sense to use direnv to automatically load variables. -- For "just source this one time", in the worst case you can run a bash shell, source that, then run a fish shell.
I also use starship with barely any custom config (like fish). It shows me the environment I'm in for a project, including which versions of tools are active and which server I'm currently connected to (if any). And it looks pretty enough.
Big one for me was out of the box support for showing current kubernetes context. Very helpful if you work in multiple kubernetes clusters and in particular if you move around namespaces helping others.
Doable with configuration and either lots of work or plugins in other shells but the combo of fish + starship.rs offers a lot for little setup.
I use starship with zsh. The biggest value is showing the current environment for any project without any config. Shows the active node version, Python venv, k8s context, all without having to think about it.
The other day an AWS command was rejected with a generic 403 error and at a glance I realized I'd forgotten to switch the default profile—without starship I suspect I would have spent a while troubleshooting my SSO login instead of just switching profiles.
I’ve used fish for nearly a decade now, it’s awesome.
The autocomplete works for so many things you don’t expect!
Command line arguments!
Remote path completion!
Git!
It’s something I install basically on everything machine I can. The history based autocomplete is so useful for keeping track of and refinding those less used complex commands that you have not saved in a function yet.
I love their tagline. Very funny. I used it for a while and liked it enough but their development strategy is explicitly against having configurable options. A few things nagged me and there was no way to change the behavior so I switched to zsh and made it work like fish, but the way I like it instead. For me this was a good decision and I recommend it. (I am not a fan of oh-my-zsh or plugin managers etc either)
can you elaborate on what nagged you? i am just curious, not critical.
i use fish, zsh and now also elvish. the main thing for me is autosuggestion and getting history with uparrow. zsh has a module for autosuggestion. elvish has uparrow history (but only matches at the start). so fish still wins over the others. elvish comes second because typing a command and hitting uparrow once is almost like autosuggestion. but i could not find a way to get that in zsh. i also prefer the way fish and elvish handle blocks over the traditional sh-shell style.
I'm unsure what specific behaviour you want from zsh and the up key, but it provides more than one up-line-*¹² function out of the box. Also, you can add any behaviour you wish with a custom zle function. That said, I understand some people prefer to just use something with the default settings.
this feature helps because in 90% of cases i can type some-word and have autosuggestion show me the right command, so i just need to hit right arrow and enter to run it. but, if the suggestion is wrong, i can instead hit up arrow and search for a another history entry.
in zsh currently, i can get autosuggestions, but if the suggestions is wrong, i have to type ctrl-A and retype some-word in order to search the history. i want to be able to do that without retyping, the way it is done in fish.
elvish only has the uparrop feature which gets me there halfway, i can type something, and then hit uparrow to find a matching command. in most cases the first hit is the one i want, but if not i keep searching.
i prefer to also have the autosuggestion because often i don't remember that i have a similar command already, and the autosuggestion provides that hint.
together these are the two most useful features that i want from a shell.
That is the difference between the up-line-or-history and up-line-or-search functions that I linked above. The default Up binding is the -history function, whereas the -search function in unbound by default. There are pattern variants too, if you don't like the prefix only checks.
If you also need to mimic the fish behaviour of C-r mid-command because your suggestion wasn't correct, then that does need a custom function to bind to C-r as you have to provide ${L,}BUFFER to populate the search pattern like fish does.
ah, thank you. that actually helps a lot. i don't use zsh as much, but it's good to know that this feature is available. i don't use ctrl-R in fish yet, so i won't miss that.
I don't remember exactly now but I have been on unixlikes forever and I am not too much into fuzzyness in general. I think it was treating upper and lower case characters the same in completions and such. Things of that nature - maybe not exactly that - but when I went to do some research about how to configure it, I realized it was not possible and that was by design. It's very good software that a lot of other shells take inspiration from but I don't agree with their methodology so I took the best bits and made zsh do them, which it can do easily. I didn't use zsh before that and now I am a huge fan.
If anybody who needs this wants to give ZSH a go, I recommend starting with only a few settings and only add new ones if you understand what they do. Same goes for plugins. I do not recommend starting with oh-my-zsh at all - which IMO is a glorified alias database anyway.
I loved fish for about 5 years. My only gripe is that going back to Mac it wouldn’t play nice with the terminal color scheme, or the dark mode switch. Anyone found a way around that?
I use iTerm2 instead of the default Terminal app. In iTerm2, you can change the impossible-to-see blue and red to more reasonable values (or do what I do and use the Dracula Pro Themes).
The biggest screw up is that fish is not bash compatible. I'm just too used to bash and am not willing to change that because there are corporate systems where I only have access to bash and nothing else; and cannot install fish.
It will just be hurtful to need to keep switching between different scripting languages; I will never be able to do so comfortably and with muscle memory.
I think this is what most fish users do. I suspect that failing to understand this is possible, due to not understanding how shebang scripts are executed by the kernel, is a major reason why bash remains popular for interactive use.
It's not that. We don't want to switch shell syntax between workstation and server, for example. The fish users I've known moved very slowly when they had to hop on a server, or build a new container image where it involved a 'docker run' into the shell to test it out. If you know bash or sh well enough to do those things quickly then there is no incentive to use fish.
But `docker run` is the same in every shell. The only things that really change is the syntax for looping over files, advanced globing, things like that. Control-R, tab completion etc all works more or less the same with some minor quirky differences, but nothing that would slow somebody down.
Context: I used bash for about 5 years, zsh for the next 10 before switching to fish.
> If you know bash or sh well enough to do those things quickly then there is no incentive to use fish.
I honestly think you might misunderstand why a lot of people use fish. I know bash “pretty well”—I’ve written and maintained my fair share of complex bash scripts, and remain pretty comfortable opening a shell in a running docker container or EC2 instance to debug various nonsense. What makes fish attractive to me for use as my login shell is the creature comforts which are very nice to have on my regular workstation, but aren’t really necessary when mucking around in some random environment: syntax highlighting, tab completion/autosuggestion/history search which “just works”, somewhat nicer syntax for writing simple scripts for personal use, saner word splitting, and so on. I guess I can’t really speak for other fish users and am perhaps biased by learned bash first a couple decades ago, but I don’t personally find it especially burdensome to have to remember multiple shell syntaxes/semantics (I already use half a dozen in my day to day work; what’s one more?), and I view it as a reasonable price to pay for a UI I find much more pleasant on whole.
No, I mean creating some kind of quick program from the prompt itself.
Like for instance bulk renaming of files from the prompt, there is no way I could do that quickly in fish's language and having to switch to bash to do that would be unnecessarily slow. Might as well stick to bash.
What I mean to say is that fish would hurt my productivity rather than boost it.
learning any new tool takes a bit of time. but i believe you are overestimating the effort it takes to learn fish syntax. it is not that hard. you also need to consider the features in fish that boost your productivity that don't need to be learned at all. just give it a try.
This is the reason I was amenable to making the jump to zsh.
With the customizations I have in my shell, my bash startup times were creeping into the multi-second territory, but I got a better UX and faster shell startup without having to learn new syntax for simple shell loops.
i use fish for interactive work and bash for scripting. if there is a script that i don't want to write in bash, then i use a real programming language like python or ruby.
I don't believe that's the goal they set out for themselves; and besides I feel like it should at least be as POSIX compatible as they could make it. They could drop POSIX oddities where they see fit, but POSIX is not all insane.
I mean, I really doubt that the `export` command is terrible somehow. In fish, you have to use `set -Ux` which is just ridiculous and petty of them, the world doesn't revolve around the fish developers and this just breaks many files that you try to source from fish; for example, Python venv activation scripts.
As for why I don't think that's their goal, just look at https://fishshell.com/ not one of the listed features requires them to drop POSIX compatibility entirely. I just want something like fish shell that isn't too radical and tries to be as POSIX compatible as possible while adding their improvements, out of the box (so ZSH doesn't count).
> As for why I don't think that's their goal, just look at https://fishshell.com/ not one of the listed features requires them to drop POSIX compatibility entirely.
You can source bash files (like .bashrc) using the plugin "bass". You can also launch fish using a bash file, just export the variables and run fish as the last command.
I personally just put what path modifications I need in .profile and have fish as my login shell.
OK so if you're new to fish you should understand universal variables since it's a concept that bash/zsh does not have and it's actually pretty great.
When people first try out fish they want to add their global env vars and PATH configuration to ~/.config/fish/config.fish. You can absolutely do this similar to how you do it in ~/.bashrc. However, it's not idiomatic and once you get used to universal variables, you'll see that it's a lot of yak shaving you really just don't need editing config.fish.
Instead, if you want to permanently set an env var across the current session and all new sessions, run: `set -Ux MY_ENV_VAR 1` (set a universal variable, and export it to subprocesses)
That will put it into a machine-readable file ~/.config/fish/fish_variables. You can open it if you want but this way you don't need to source your rc files, open a new shell, or even open your editor.
For PATH, just run: `fish_add_path ~/my-new-bin`. It uses universal variables by default.
For some reason I avoided universal variables as much as possible for years using fish and now I think that was really silly.
They're really completely different. Fish is mostly about having features that help you enter a command (history, error checking, completions, etc). Once you execute something, it really behaves the same as bash/zsh.
Nushell is really more about command output (my understanding, I haven't used it heavily). With Nushell you can do things like parse TOML files right in your shell:
open Cargo.toml | get package.version
Nushell understands the structure of data but fish is like bash/zsh in that it just deals with streams of text. Nushell can even interact directly with sqlite databases which is very cool.
I think if you're just looking for a simple shell that behaves a lot like bash/zsh, use fish. I think Nushell is more targeted to people that want to use its powerful data pipeline features—or are generally interested in a bigger departure from bash/zsh.
Personally I prefer fish, but I should start using Nushell more since it could definitely help with some tasks. I probably would just run Nushell when I need it though, I don't see myself running chsh to switch over.
I don't use fish but it does have one great feature that I'm surprised hasn't been replicated in zsh: automatic completions derived from man pages. So you get at least basic flag completions for basically every program automatically.
I've been using Fish as my login shell for more than a decade now. Unlike many other commenters here, I also use it for scripting. For simple stuff, it's way nicer for writing scripts because the language just has has fewer gotchas than bash.
If you're just reading instructions online, translating between examples given in bash and fish is trivial. I see people talk about that being an issue for them and I don't understand it; it's literally never given me trouble.
The one thing that keeps me from using it more often for scripting is the lack of a fail-on-error mode (similar to `set -e` on bash). I know it is hard to get this to behave well in all cases (e.g. pipefail) but I don't want to do much scripting without it.
The other ones that annoy me are shell redirection from fish functions and some job control issues, which I'm hoping will both be fixed when job control is rearchitected as part of the ongoing Rust port.
I still use bash in CI, and for Linux scripts that I distribute to others. For macOS automation, I use zsh since it's the default nowadays.
My fish scripting is mostly for personal use and occasionally little wrappers on servers that I own. Sometimes I'll share a bit of fish code with other fish users at work, too.
Fail-on-error would be nice and help make the case for using fish in more places. At organizations where no one really knows bash that well anyway, fish would actually be nicer for scripting than bash if it had just a few more goodies like that imo.
I quit fish because I missed being able to `sudo !!` to rerun the previous command with sudo. There's an alternative way to do it (something like up arrow, ctrl-i, "sudo", enter), but I can never remember it when I need it, which is not often enough to stay in my head, but often enough to be come quite annoying.
That is a recent(-ish) addition¹, arriving in 3.1. Your interpretation of recent may be a different length to mine, but one Debian release in this instance ;)
I guess he wasn't aware. I use fish, and I'm learning this now too. I probably really should give the fish docs a read-through, as I imagine it could significantly improve my shell efficiency.
Glad to hear that's changed. There were strident statements from the developer on Github last time I researched it to the effect that implementing '!'-style history expansion was contrary to the fish philosophy and that people just need to learn the fish-y way. Looking back it, I probably could have fixed the issue for myself by setting up vi-mode in fish, but I didn't come across that option at the time.
I also use `!$` (`vim script.py` and then `python !$` or `git commit !$`) but the parser rejects `!$` before it can be rewritten by the abbreviation system. [edit: https://superuser.com/a/1762626 points out that if you add a space before hitting enter then it works fine, so I'm guessing it's just a bug, and I should go with d below]
Options seem to include:
a) make a !$ replacement that is not illegal and change your muscle memory `\$` or `!\$` or `!@` or `!%`
c) some combination of a and b (e.g. make a `!\$` abbreviation and then make a binding so that if you type !$ it replaces it with `!\$` so that it gets past the parser without expliding)
d) patch the parser to allow !$ as a special case if there is an abbr for it.
I just tried b but it's pretty jarring. I think I might go with c instead.
I'm actually feeling quite positive about this now.
There is something I've been wanting to add to bash since forever, which is something to help me cd into a repo that I just cloned (e.g. `git clone https://github.com/fish-shell/fish-shell` then `cd !/` could expand to `cd fish-shell`).
Bash has a range of modifiers for history expansion. You can "cd !$:t" to execute "cd <final argument of last command, basename only>" for your git example. zsh, somewhat predictably, has a superset of bash's modifiers so it works there too.
I tried zsh at some point, but I got the impression that it's basically the same as bash out of the box, and you need to spend time customizing it to see the benefits. I don't want to spend time customizing my shell, I want it to work out of the box.
Fish is noticeably better than bash out of the box, with absolutely no customization. Magic autocomplete is life-changing.
I was hesitant to switch to fish shell, but after seeing so many recommendations on HN, I gave it a shot. Been using it for a while now, and it's mostly good.
The one hiccup is the syntax; it's different from ZSH, so you have to update your existing config. ChatGPT has been pretty useful for that, though.
At least now && works. It used to not be the case and required you to type ";and" which was incredibly annoying as you had to modify basically every single command that you found online or that people sent you.
Now that && works, I don't have any big conplaints about it anymore.
Fish is pretty good, but I wish it would come with fzf-like capabilities built into it by default, for automatically searching history/files/etc.
Yes I know I can just install fzf and the fzf/fish integrations, I've done so. But my understanding is that the whole point of fish is that it comes well configured by default. fzf is a quantum leap forward for shell UX, this sort of functionality should be built in by default.
I may be mistaken, but since I have to work with different machines that all have bash as a shell, I try not to mess with my muscle memory and stick to bash even on my own machine. But maybe I should try Fish.
I was using fish on Ubuntu in college. First job gave me a macbook and I set it up using fish. We only had one devops guy and he shared the app set up docs and all the scripts were available only in zsh or bash. I was okay with switching to zsh tbh. But the dude stopped me and then spent half a day learning and then updating all the docs with fish scripts as well! Bless him!
I don't know much about shells. Fish was the first one that made everything so easy and the only downside was not being posix compliant, but i can easily call bash and run the command. I tried leaving fish and the others requires so many configurations, but fish does by default. It suits me.
Best part of fish is how little config is needed. I’m 41, married with kids. Old AF by some standards. I have zero patience with wasting my time configuring tooling.
zsh felt like it fought me every step of trying to configure it to be halfway decent. And a lot of zsh scripts out there aren’t exactly bulletproof. It shouldn’t matter, except for the fact that if you use the shell with any regularity you will inevitably bump into edge cases that weren’t handled well by a mishmash of user scripts whose provenance is mostly copy/paste with little understanding of why it is all needed.
It has a submodule based plugin system (loader in conf.d), an async git prompt, my aliases, and a few env vars set. It needs no maintenance because fish provides almost everything already.
The only thing I want from fish at this point is to make async prompts first-class. They are best handled by the shell, not user code, due to the state tracking.
Agree with everything you said. If I didn't have to write /bin/sh compatible scripts for work, I would switch to Fish for similar reasons. From a UX point of view it's indisputably ahead of every other shell and it isn't even close, to the point I'm surprised it hasn't been adopted as the default by an OS like Mac yet. I can walk my family members through terminal things with Fish and nobody gets frustrated or quits. I can't say that about other shells.
I use bash and also have no patience for configuring tooling. Your fish dotfiles are more complicated than my bash ones. I used to work on a team where many of the other engineers used fish. I never saw it doing anything I couldn't do in bash, although it looked a little fancier. But the annoying thing was when they asked me for help, and none of the commands I gave them worked in fish. It made for very slow troubleshooting sessions.
i am using fish for more than a decade and i don't have any dotfiles for it. (do i win? :-p )
your commands to help not working in fish? that is rather surprising. i would think that fish users should be aware that shell specific things in fish are different, so they should have been able to cope and translate as needed.
and it's not what you can do in fish that makes it special. it's how you do it. the syntax coloring, the completions, the syntax itself...
the key thing for me is how it handles searching history and autosuggestions. i just learned how to somehow recreate this setup in zsh, and i am very curious if bash can do that too: https://news.ycombinator.com/item?id=37274207
btw i believe bash got a lot better at command completion after fish popularized the idea.
> zsh felt like it fought me every step of trying to configure it to be halfway decent.
oh-my-zsh is a lifesaver here. I have a very small dotfile that delegates most of the config to omz, which I have in a bare repo that I clone onto each new machine. It took maybe an hour a few years ago to settle on the plugins I wanted and put the repo together, and each new machine now takes 5-10 minutes to get up and running.
I went through the initial config wizard of zsh exactly once, almost a decade ago[1]. It took me maybe 5-10 minutes, and even though I don't fully understand what each command means, I haven't modified it in all this time, and it works well. I really don't think this was any significant waste of time or tedious task - I was just as free to simply ignore it.
Most of the remainder of my zsh config are prompt utilities and other things I've added over the years. I don't use or really need any plugins, though if I needed, last time I tried zplug it was really easy to set up and use.
169 comments
[ 25.7 ms ] story [ 5079 ms ] threadI add starship.rs to it and fzf integration but that's it.
A shell theme
Fish conveniently provides history based autocomplete so I don't need to setup some plugin for that.
If you often share shell code with others it may be to big of a leap for you.
https://github.com/edc/bass
https://github.com/jorgebucaran/fisher
1: https://www.shellcheck.net/
if someone is saying they can’t run a bash script when they’re invoking bash from fish the most likely thing is they’re doing something wrong.
Edited to add:
There's a program out there for translating bash to fish called babelfish. It's pretty good but not complete, so it won't work for all scripts. Worth checking out, though:
https://github.com/bouk/babelfish
The specific issue was to do with a script we had to run to gain ssh access to an internal network. I can't post the script, but it was related to openssh.
Even logging into bash and running the script didn't work, I had to remove all fish binaries, symlinks, etc and set my shell to bash/zsh. Maybe it was an issue with how I installed fish, but yeah like I said, very strange, but I can vouch that I've encountered a similar issue before
It's a real shame because I think the fish syntax alone is worth the switch, for my personal machine(s) I use it.
Edit: this was years ago now, about 4 years, maybe the issue doesn't exist anymore but it caused me a bit of grief at the time (beacuse I didn't know it was related to using fish!), I've not since tried it again. I might try again and get back to this thread
although such a name conflict should not have happened (and i can't think of which command such a script might have used that would also be a fish command), and the global path thing was also fixed soonish. but this is all a faint memory, so i am not sure i remember any of that right.
Not sure what the point of distinguishing between fish builtins and fish functions is; whether a builtin is shipped as a function distributed with fish or a reserved word in the fish evaluator seems like an implementation detail.
Builtin commands should only be created when it cannot be avoided. echo, kill, printf and time are among the commands that fish does not implement internally since they can be provided as external commands. Several other commands that are commonly implemented as builtins and can not be implemented as external commands, including type, vared, pushd and popd are implemented as shellscript functions in fish.
if i remember correctly, this led to some useful commands that are builtin elsewhere to be external binaries shipped with fish. but since those where not actually tied to the fish shell they could run without it, and if they ended up on the global path be accessible from other shells.
the relevant text on the website has been changed, but it is referenced here:
https://github.com/fish-shell/fish-shell/issues/612
the discussion also points out that this has changed over time
If you wanna add a builtin like that to your own distribution of fish, you could do it cleanly by keeping those binaries in /usr/lib or /usr/libexec and then wrapping them in a fish function that ships in fish's install prefix. (This is basically how fish's Python scripts for generating completions from manpages are shipped today.)
right, i don't remember the details, but when it happened it was probably fixed quickly. could even have been a packaging error in a distribution.
Most likely a command in your script (or one source-d into it) makes an assumption about your $SHELL or login shell that is true for bash/zsh but not for fish.
Merely adding a shebang won't fix such a script.
If it ends up passing a script file or arguments to $SHELL and you wanna fix it without rewriting it, just add some conditionals where $SHELL is used and use the equivalent flags or syntax when $SHELL indicates fish.
Alternatively, if you just wanna work around it, just launch that script like
are you sure you're running `./scriptname` or bash scriptname`
You mention elsewhere that this had to do with a special OpenSSH setup, which also fits.
One of the things you can do with ssh-agent is use it to launch a child process with a dedicated SSH agent that (only) has certain keys on it, and which exits once its child exits. This is sometimes handy for deployment scripts or doing git checkouts or whatever because you can ensure you don't get locked out for too many auth attempts because the user just has too many extraneous keys on the agent associated with their normal user session.
If you were leveraging this feature for interactive shells, you might be tempted to use $SHELL to decide what executable to have ssh-agent launch, so that you could (for example) accommodate both bash and zsh users and let them launch a shell with a special SSH agent but which honors their usual preferences by loading their usual shell and reading their usual zshenv or bashrc or whatever it is.
You mention as well that you had to actually uninstall fish to get things to work again, and also that you were on macOS. I can't be certain about the cause here, but one obvious thing occurs to me:
macOS doesn't handle environment setup like any 'normal' (Linux or *BSD) Unix. On normal Unices, your login shell actually also launches your graphical user session, so to configure a session-wide environment variable, you just set it in your shell startup somewhere. On macOS, login shells are actually only used in SSH sessions and terminal emulators. If you want to set environment variables for apps that are not launched from terminal emulators, you have to use hacks (like Doom Emacs' env file, for example) or configure them for the whole user session via a LaunchAgent.
In the case of $SHELL, that environment variable is used by editors to determine what shell to use when you run external commands. Without it, if you are a fish user and you launch MacVim or Emacs (or, presumably, VSCode or anything else) from your dock, when you go to launch external commands, they will run not in fish but some other shell (probably zsh or bash). If your install method for fish tried to handle this for you (pkgsrc and Nix don't, but the .pkg from the developers or Homebrew might, idk), or if you dropped to bash only from terminal emulator windows that were already running fish instead of opening a new session for bash, you may have wound up in a bash session where $SHELL was still set to fish. That's the only reason I can think of for why you might have had to actually uninstall fish to get this ill-behaved script to work right.
Anyway:
1. As a user, pay attention to what $SHELL is.
2. As a script author, never use `eval "$SHELL ..." or equivalent`.
That's no longer true for Linux, at least. GDM launches stuff via systemd user sessions, which do not source your profile.
I'm not sure how I feel about it. The old way feels 'simple' to me, but the new way does mean that shell misconfiguration won't hose your GUI sessions.
I use xonsh and fortunately it has source-bash for stuff like that.
https://github.com/PatrickF1/fzf.fish
Can also do cool stuff like search for a file to open in $EDITOR and searching git log
Longer scripts get the Python treatment or a Go application.
You can run bash scripts from fish.
For sourcing, often it makes sense to use direnv to automatically load variables. -- For "just source this one time", in the worst case you can run a bash shell, source that, then run a fish shell.
There are some optional bits for more niche things (like laptop battery) you can enable with a config file, but it’s not necessary.
Doable with configuration and either lots of work or plugins in other shells but the combo of fish + starship.rs offers a lot for little setup.
The other day an AWS command was rejected with a generic 403 error and at a glance I realized I'd forgotten to switch the default profile—without starship I suspect I would have spent a while troubleshooting my SSO login instead of just switching profiles.
Command line arguments! Remote path completion! Git!
It’s something I install basically on everything machine I can. The history based autocomplete is so useful for keeping track of and refinding those less used complex commands that you have not saved in a function yet.
i use fish, zsh and now also elvish. the main thing for me is autosuggestion and getting history with uparrow. zsh has a module for autosuggestion. elvish has uparrow history (but only matches at the start). so fish still wins over the others. elvish comes second because typing a command and hitting uparrow once is almost like autosuggestion. but i could not find a way to get that in zsh. i also prefer the way fish and elvish handle blocks over the traditional sh-shell style.
¹ https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#...
² https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#...
this feature helps because in 90% of cases i can type some-word and have autosuggestion show me the right command, so i just need to hit right arrow and enter to run it. but, if the suggestion is wrong, i can instead hit up arrow and search for a another history entry.
in zsh currently, i can get autosuggestions, but if the suggestions is wrong, i have to type ctrl-A and retype some-word in order to search the history. i want to be able to do that without retyping, the way it is done in fish.
elvish only has the uparrop feature which gets me there halfway, i can type something, and then hit uparrow to find a matching command. in most cases the first hit is the one i want, but if not i keep searching.
i prefer to also have the autosuggestion because often i don't remember that i have a similar command already, and the autosuggestion provides that hint.
together these are the two most useful features that i want from a shell.
If you also need to mimic the fish behaviour of C-r mid-command because your suggestion wasn't correct, then that does need a custom function to bind to C-r as you have to provide ${L,}BUFFER to populate the search pattern like fish does.
If anybody who needs this wants to give ZSH a go, I recommend starting with only a few settings and only add new ones if you understand what they do. Same goes for plugins. I do not recommend starting with oh-my-zsh at all - which IMO is a glorified alias database anyway.
It will just be hurtful to need to keep switching between different scripting languages; I will never be able to do so comfortably and with muscle memory.
Context: I used bash for about 5 years, zsh for the next 10 before switching to fish.
I honestly think you might misunderstand why a lot of people use fish. I know bash “pretty well”—I’ve written and maintained my fair share of complex bash scripts, and remain pretty comfortable opening a shell in a running docker container or EC2 instance to debug various nonsense. What makes fish attractive to me for use as my login shell is the creature comforts which are very nice to have on my regular workstation, but aren’t really necessary when mucking around in some random environment: syntax highlighting, tab completion/autosuggestion/history search which “just works”, somewhat nicer syntax for writing simple scripts for personal use, saner word splitting, and so on. I guess I can’t really speak for other fish users and am perhaps biased by learned bash first a couple decades ago, but I don’t personally find it especially burdensome to have to remember multiple shell syntaxes/semantics (I already use half a dozen in my day to day work; what’s one more?), and I view it as a reasonable price to pay for a UI I find much more pleasant on whole.
Like for instance bulk renaming of files from the prompt, there is no way I could do that quickly in fish's language and having to switch to bash to do that would be unnecessarily slow. Might as well stick to bash.
What I mean to say is that fish would hurt my productivity rather than boost it.
With the customizations I have in my shell, my bash startup times were creeping into the multi-second territory, but I got a better UX and faster shell startup without having to learn new syntax for simple shell loops.
https://github.com/edc/bass
personally I almost never put anything that complicated into my shell, once it gets to that point it goes into a script.
You can't make a sane shell scripting language without dropping POSIX compatibility.
I mean, I really doubt that the `export` command is terrible somehow. In fish, you have to use `set -Ux` which is just ridiculous and petty of them, the world doesn't revolve around the fish developers and this just breaks many files that you try to source from fish; for example, Python venv activation scripts.
As for why I don't think that's their goal, just look at https://fishshell.com/ not one of the listed features requires them to drop POSIX compatibility entirely. I just want something like fish shell that isn't too radical and tries to be as POSIX compatible as possible while adding their improvements, out of the box (so ZSH doesn't count).
“Sensible scripting” is right there.
I personally just put what path modifications I need in .profile and have fish as my login shell.
When people first try out fish they want to add their global env vars and PATH configuration to ~/.config/fish/config.fish. You can absolutely do this similar to how you do it in ~/.bashrc. However, it's not idiomatic and once you get used to universal variables, you'll see that it's a lot of yak shaving you really just don't need editing config.fish.
Instead, if you want to permanently set an env var across the current session and all new sessions, run: `set -Ux MY_ENV_VAR 1` (set a universal variable, and export it to subprocesses)
That will put it into a machine-readable file ~/.config/fish/fish_variables. You can open it if you want but this way you don't need to source your rc files, open a new shell, or even open your editor.
For PATH, just run: `fish_add_path ~/my-new-bin`. It uses universal variables by default.
For some reason I avoided universal variables as much as possible for years using fish and now I think that was really silly.
[1]: https://github.com/atuinsh/atuin
Nushell is really more about command output (my understanding, I haven't used it heavily). With Nushell you can do things like parse TOML files right in your shell:
Nushell understands the structure of data but fish is like bash/zsh in that it just deals with streams of text. Nushell can even interact directly with sqlite databases which is very cool.I think if you're just looking for a simple shell that behaves a lot like bash/zsh, use fish. I think Nushell is more targeted to people that want to use its powerful data pipeline features—or are generally interested in a bigger departure from bash/zsh.
Personally I prefer fish, but I should start using Nushell more since it could definitely help with some tasks. I probably would just run Nushell when I need it though, I don't see myself running chsh to switch over.
If you're just reading instructions online, translating between examples given in bash and fish is trivial. I see people talk about that being an issue for them and I don't understand it; it's literally never given me trouble.
I still use bash in CI, and for Linux scripts that I distribute to others. For macOS automation, I use zsh since it's the default nowadays.
My fish scripting is mostly for personal use and occasionally little wrappers on servers that I own. Sometimes I'll share a bit of fish code with other fish users at work, too.
Fail-on-error would be nice and help make the case for using fish in more places. At organizations where no one really knows bash that well anyway, fish would actually be nicer for scripting than bash if it had just a few more goodies like that imo.
> `Alt` + `S` Prepends sudo to the current commandline. If the commandline is empty, prepend sudo to the last commandline.
https://fishshell.com/docs/current/interactive.html#shared-b...
¹ https://github.com/fish-shell/fish-shell/pull/6140
This post has prompted me to give fish another go, so I looked into it again.
It looks like the abbreviation system can now (since March this year) help you with !! (https://fishshell.com/docs/current/cmds/abbr.html even gives it as an example)
I also use `!$` (`vim script.py` and then `python !$` or `git commit !$`) but the parser rejects `!$` before it can be rewritten by the abbreviation system. [edit: https://superuser.com/a/1762626 points out that if you add a space before hitting enter then it works fine, so I'm guessing it's just a bug, and I should go with d below]
Options seem to include:
a) make a !$ replacement that is not illegal and change your muscle memory `\$` or `!\$` or `!@` or `!%`
b) use a keybinding for !$ as suggested in https://github.com/fish-shell/fish-shell/issues/288
c) some combination of a and b (e.g. make a `!\$` abbreviation and then make a binding so that if you type !$ it replaces it with `!\$` so that it gets past the parser without expliding)
d) patch the parser to allow !$ as a special case if there is an abbr for it.
I just tried b but it's pretty jarring. I think I might go with c instead.
I'm actually feeling quite positive about this now.
There is something I've been wanting to add to bash since forever, which is something to help me cd into a repo that I just cloned (e.g. `git clone https://github.com/fish-shell/fish-shell` then `cd !/` could expand to `cd fish-shell`).
Exciting times.
Fish is noticeably better than bash out of the box, with absolutely no customization. Magic autocomplete is life-changing.
I tried a lot of shells and that's what I ended up with.
The one hiccup is the syntax; it's different from ZSH, so you have to update your existing config. ChatGPT has been pretty useful for that, though.
Now that && works, I don't have any big conplaints about it anymore.
Yes I know I can just install fzf and the fzf/fish integrations, I've done so. But my understanding is that the whole point of fish is that it comes well configured by default. fzf is a quantum leap forward for shell UX, this sort of functionality should be built in by default.
zsh felt like it fought me every step of trying to configure it to be halfway decent. And a lot of zsh scripts out there aren’t exactly bulletproof. It shouldn’t matter, except for the fact that if you use the shell with any regularity you will inevitably bump into edge cases that weren’t handled well by a mishmash of user scripts whose provenance is mostly copy/paste with little understanding of why it is all needed.
Here’s my fish dotfiles:
https://github.com/mattgreen/dotfiles/tree/master/fish
It has a submodule based plugin system (loader in conf.d), an async git prompt, my aliases, and a few env vars set. It needs no maintenance because fish provides almost everything already.
The only thing I want from fish at this point is to make async prompts first-class. They are best handled by the shell, not user code, due to the state tracking.
your commands to help not working in fish? that is rather surprising. i would think that fish users should be aware that shell specific things in fish are different, so they should have been able to cope and translate as needed.
and it's not what you can do in fish that makes it special. it's how you do it. the syntax coloring, the completions, the syntax itself...
the key thing for me is how it handles searching history and autosuggestions. i just learned how to somehow recreate this setup in zsh, and i am very curious if bash can do that too: https://news.ycombinator.com/item?id=37274207
btw i believe bash got a lot better at command completion after fish popularized the idea.
oh-my-zsh is a lifesaver here. I have a very small dotfile that delegates most of the config to omz, which I have in a bare repo that I clone onto each new machine. It took maybe an hour a few years ago to settle on the plugins I wanted and put the repo together, and each new machine now takes 5-10 minutes to get up and running.
Really feels like OMZ doesn’t know its place when it is configured by default to do that. Tools should be quiet and keep to themselves.
For example, compare this in zsh:
``` case $input in "foo") echo "Input is foo" ;; "bar") echo "Input is bar" ;; ) echo "Input does not match any case" ;; esac ```
with this in Fish shell:
``` switch $input case "foo" echo "Input is foo" case "bar" echo "Input is bar" case "" echo "Input does not match any case" end ```
And it was a really small example. Fish can do much more with "simpler" and not arcane syntax.
Fish also has `string` to work with string much much easier [0].
You miss `oh-my-zsh`? Fish has `oh-my-zsh` [1].
You miss integration with `fzf`? Fish also has it.
One caveat is Fish is not POSIX-compatible. But I don't find it a compelling reason to not using it.
Personally, I use Fish as my main shell. I do use zsh/Bash in my company though since they're more ubiquitous.
[0] https://fishshell.com/docs/current/cmds/string.html
[1] https://github.com/oh-my-fish/oh-my-fish
Most of the remainder of my zsh config are prompt utilities and other things I've added over the years. I don't use or really need any plugins, though if I needed, last time I tried zplug it was really easy to set up and use.
[1]: https://github.com/mid-kid/config/blob/master/shell/.zshrc#L...