While from what I have read about it zsh is a nice shell, it doesn't deal with what I find to be the biggest problem with bash, sh, and just about every shell that isn't fish: the horribly ugly and inconsistent syntax. And I think it's rather sad that on what is supposedly the most advanced shell of our generation, you still have to deal with a thousand ridiculous redirections and quoting styles, different delimiter keywords for every control structure ("fi?" "done?" "esac?" Why not just freaking "end?"), and rules for expanding variables that come back to bite you in the rear whenever you have spaces, quote characters, or anything besides a run of letters, numbers, and a select few punctuation marks in your filenames or other variables.
I have been using fish for several years now. It's community is a fraction of zsh, bash, and others, and I've tried to go back to them, mostly out of misplaced "everyone else is doing it" mindset
Fish is a great shell. It's a modern shell, that uses colors, UTF, and processing power that didn't exist when the others were designed. I feel the best thing about it is the sane defaults and large number of prebuilt completions. For any other shell, I have to dive into bashrc's and esoteric commands to get what I want. Fish just does what I want. It's made me far more productive, and for that, it gets my best shell award.
Its scripting support is good, very similar to other shells. Yes, the syntax is non-compatible, and not even POSIX. But, it makes more sense. Besides, for anything except the simplest of scripts, I use lua. I doubt you'd want to use bash to do something very complicated.
Some people want their shell scripts to be compatible and do a lot of cool things. I feel that the days of a shell being used for scripting are past. For me, a shell is a way of interacting with the system. Fish does this really well. Scripting is a slight afterthought, as it should be.
I feel that the days of a shell being used for scripting are past.
I've noticed this attitude among those who tend to use sysadmin-minimizing features of the internet, such as cloud hosting. There's still a whole world of real server out there, though.
I get what you're saying but have to say that since I've started doing more deployments in AWS I'm writing more and more shell scripts. Any given Linux AMI will support Bash and even though shell scripts are painful to write and maintain, doing the equivalent in e.g. Python is painful as well :-(
That is true. I do minimal system administration, so that is not a concern for me.
I tend to use a shell for system interaction more than scripting. In that regard, I give more importance to interactivity enhancements. On the other hand, I have found myself using bash to automate simple things in our company testbed. As soon as I need a datastructure more than an array, or recursive functions, I jump to lua.
If your primary job is to be a sysadmin, then Bash is Required Reading.
I disagree. As a programmer I can represent inputs and outputs as text and write code to do intended transformations. As a quick ad hoc test I can easily knock up a shell pipeline to give confidence in those results, even if it would be too slow for production. There's many occasions when powerful shell usage is quicker to write than Python, etc.
!! is not specific to fish. It is a history feature to get the last command. It's like using '!ec' for getting the last command that starts with 'ec'. Using `!!` is not "getting output of previous command", it is re-executing it and getting that output.
You haven't told me how to do a `!!` in fish. (I do understand that it re-executes the previous command). I am used to using this in other shells like bash and zsh, and would like to know if there's some way of doing it in fish.
AFAIK this is not possible with fish, as variables cannot be used as commands like in bash (and the ! history feature does not exist in fish, it is designed to be fully interactive).
Yes, I just use the builtin's. Basically os.execute, and io.popen.
Popen is not the greatest because you can't get exit statuses from it. But for a quick script that is too messy in shell, it suffices. I believe that there is an extension to lua that has a popen that returns exit statuses. That can be used too.
For pattern matching, I either pipe to perl and capture the output, or use the inbuilt pattern matching system. It's surprisingly powerful.
You can also get the exit status using Lua 5.1 but it's definitely nasty (io.popen('your-command; echo $?') and the last line of output is the exit status).
As long as the people designing fish believe that, it will be unsuitable for people who want or need customized solutions. That describes a lot of people using shells now.
Once you learn the zen of shell script, like any programming language, those problems pretty much go away. Shell script is a tiny language packed with features and can be learned through constant use in a week, but like any language, it takes time to master.
It's incredibly powerful for interactive use and data manipulation combined with the standard Unix tools and sometimes pulling out perl or python is more work than just banging out a pipeline on the command line.
Quoting does take some time, but just remember that when in double to quote your variable expansions with double quotes. There's more too it, but that's a good rule to start with.
One of my favourite aspects of rc is the sane array handling.
In bash: $array expands to the _first_ element of the array; you have to say ${array[@]} to get the whole array. And then you have to quote it in case one of the elements has spaces.
In rc: $array expands to the whole array, and the expansion is _not_ re-parsed, so if an element of the array has spaces in it, it is still considered a single element after the array variable expansion.
For this reason rc doesn't even need double-quoting; it only has single-quoting for escaping spaces in string literals.
The rc example is worse than the first of yours. I hate the zsh special syntax in the second: it violates a deeply ingrained code feel I have about the syntax of shell code. The do ... done syntax helps the eye.
rc really shines when you have problems that in bash or zsh would suggest using multiple layers of expansion. The semantics of rc's expansion is much less thorny.
I prefer real examples - here, why not just write
seq 1 100
- what is the loop for? And shell loops can frequently (always, if you are willing to write helper scripts) be replaced with xargs, which tends to be very much faster, e.g.
seq 1 100 | xargs echo
(which doesn't quite give the same output, but passing -n1 to xargs fixes that)
Honestly, I find that stuff pretty easy to remember. And is it that ugly, in the grand scheme of ugly shell syntax? :)
I mean, what about [[ ]] vs. (( )) vs. [ ]? Or the beginning of a case statement? Those always get me since I don't use them often enough for them to stick.
All of the other syntax is shell-specific (run via built-ins), so that can vary, and be a little more obtuse.
[1] Albiet with some minor differences:
% if [ -d /tmp ]; then echo "TRUE"; fi
TRUE
% if test -d /tmp ]; then echo "TRUE"; fi
test: too many arguments
% if test -d /tmp; then echo "TRUE"; fi
TRUE
% diff /usr/bin/{test,\[}
Binary files /usr/bin/test and /usr/bin/[ differ
% man \[
No manual entry for [
The thing I like about fish is the easy history nav. I type "git" and press Up, and I get to go through my history of anything that started with "git" very quickly. I'll often remember I typed an svn command three weeks ago, but forget the parameters, and I'll have it back within 15 seconds.
the problem is there are really two things that most shells are trying to simultaneously optimize for--interactive and scripted use. sometimes these conflict in annoying ways--i think the main reason arrays are so obnoxious to use in bash is that their syntax collides badly with glob syntax.
some shells that make excellent script languages are terrible for interactive use--a stock ksh, even ksh93, is pure torture for someone who grew up on bash and zsh and expects completion, history, a nice prompt, decent command editing, etc.
(i'm not sure if the reverse is precisely true, though certainly there are interesting programming features in ksh that are absent in zsh--discipline functions and so on.)
I think you're underestimating how frequently some folks do things like:
for x in 'seq 1 100'; do echo "something with $x"; done
(Please forgive lack of backticks on my cellphone keyboard)
Or many variations, like every line in a file, or result of a command piped to sed or awk and then processed. Init scripts are about the only shell scripts I write anymore, but I use frequently use it from the commandline, typically on remote terminals.
I now always use $(cmd) instead of `cmd`. Much easier to read and it fits in better with process substitution <(cmd). More importantly it all nests sanely $(cmd1 $(cmd2)) and works well in quotes "Blah: $(cmd1 $(cmd2))".