Why I prefer fish over bash for scripting
Scripting with fish
is simple. It is readable. You don’t need to have deep experience with it in order to understand the syntax.
Compared to bash
, it feels intuitive.
Everything is a builtin
builtins
are commands that you call from the shell, but unlike regular commands that execute external binaries and spawn new processes, they are built right into the shell (e.g. echo
, math
, set
, count
).
The core syntax of fish
is very simple, and most all of the operations are done via calling builtins.
Want to set a variable? Use the set
builtin. Want to declare a function? Use the function
builtin. Want perform some sort of calculation? Use the math
builtin. Want to find out what some other builtin does? Use the help
builtin.
In fact, prior to fish 3.0
, even conditional command execution was done using and
and or
builtins (unconditional command chaining is still done using the seimicolon ;
):
1$ git commit; and git push
(it still is, though support for &&
and ||
syntax was added).
String replacement
In bash
, string replacement is done using the ${$text//pattern/replacement}
syntax. For example, if you wanted to replace every instance of 'foo'
with 'bar'
in a certain text, you could do it like so:
1replaced_text=${$text//foo/bar}
In fish
, however, this is done using the string
builtin:
1set replaced_text (string replace -a foo bar $text)
If it wasn’t for the variable name, newcomers would have absolutely no idea what the fuck was going on in the bash
version, whereas the fish
versions almost completely “spells out”
the entire operation.
Array item count
In fish
, getting the number of items in an array is done using the count
builtin:
1set N (count $array)
In bash
, the syntax is, to no surprise, more obscure:
1N=${#args[@]}
Math
In fish
, mathematical operations are peformed using the math
builtin, and unlike in bash, they support decimal numbers as well as functions:
1$ echo '3 + sqrt(4) = ' (math 3 + 'sqrt(4)')
2
33 + sqrt(4) = 5
Since bash
doesn’t support decimal operations nor mathematical functions, you’d have to call external processes to do the work for you.
For someone who isn’t familiar with shell scripting, the fish
versions of these example operations are going to make much more sense at first (and probably second) glance.
This philosophy makes scripts insanely readable.
Argument substitution is more intuitive
This was one of my first serious gripes back when I was first learning bash
.
Let’s assume that barg
is a readily available program that simply prints its commandline arguments (as well as the number of them), to standard out, one per line:
1$ barg 1 2 3
2
3User arguments passed: 3
4
51
62
73
Parameter substition in fish
makes sense.
Expanding a single variable results in a single argument on the commandline. For example:
1$ set arg "Hello there, you!"
2$ barg $arg
3
4User arguments passed: 1
5
6Hello there, you!
It is only if you pass in an array of multiple values, that the resulting commandline ends up with multiple values, for example:
1$ set args "Hello there, you!" "--some-long-option"
2$ barg $args
3
4User arguments passed: 2
5
6Hello there, you!
7--some-long-option
Again, it is intuitive and it makes sense.
Now let’s take a look at the same examples, but in bash
:
1$ arg="Hello there, you!"
2$ barg $arg
3
4User arguments passed: 3
5
6Hello
7there,
8you!
The source operand was split on spaces, and a single variable substitution resulted in 3 different commandline arguments.
To avoid this, you have to encapsulate substituion in double quotes (" "
):
1$ arg="Hello there, you!"
2$ barg "$args"
3
4User arguments passed: 1
5
6Hello there, you!
To me this makes no sense, however from a historical point of view I can see why it is: in fish
, declaring arrays of multiple values each of which expands to a signle value is easy, unlike in bash
, where, if you’d written something like arguments="-a -D --long-opt"
, you would probably in fact want it to expand to 3 different arguments.
Conclusion
These are just some of the examples. The true strength of fish
lies in its power as an interactive shell - robust automatic completion, autosuggestions, configurability, colors, etc…