TL;DR: Use the function approach for the simplest of commands, and switch to an external script once you have any kind of issue with quoting. Just avoid sh -c
all together.
We can have a look at the git source. It parses out quotes in a git
specific way, executes as a file if the result has no shell metacharacters (including space), or executes the equivalent of execlp("sh", "-c", "result \"$@\"", "result \"$@\"", args...)
otherwise.
Based on that, the best way to create an alias is definitely to create an external script and using:
[alias]
myAlias = !/path/to/script
It requires an external file, but in all other ways it's better:
- You run with any interpreter you want. You can even run
bash
code since you ask, while the other forms only allow you to run sh
code (the difference is like C++ vs C).
- No additional shell is started unless you want it to.
git
will execve
your executable directly.
- No escaping is required. It's just a normal script.
- No surprises or
git
knowledge needed. It's just a script.
Out of your suggestions, the runner up is:
[alias]
fAlias = "!f() { ... ; }; f"
This is less awesome because:
- You unfortunately can only run with
sh
, and therefore not use any bash
features.
- A shell is always started, but at least it's just one.
- Some
git
specific escaping is required for quotes.
- You need to be aware of
git
's escaping, but can generally get by without knowing exactly how it executes the command because you'll just use the function's arguments.
The last one is by far the worst:
[alias]
shAlias = !sh -c \" ... \"
It's awful because:
- You can only run with
sh
.
- It'll start an extra shell for no good reason.
- You need both
git
escaping and also an additional level of sh
escaping.
- You need to know both how to escape code for
sh
, how to double-escape that with git
, and how git
appends "$@"
to your command if there are additional arguments, and passes additional parameters as $1
, $2
.
To demonstrate the practical difference, let's say you wanted to make an alias for this command to get the path on a remote server via ssh
:
ssh "$1" 'echo "$PATH"'
You can either copy-paste it verbatim to a file, add a shebang, and use:
[alias]
get-remote-path = !/path/to/my/script
Or you can add a bit of git
escaping and use the function approach:
[alias]
get-remote-path = "!f() { ssh \"$1\" 'echo \"$PATH\"'; }; f"
Or we can laboriously escape and escape again with the sh -c
approach (no, it doesn't work without the last word):
[alias]
get-remote-path = !sh -c 'ssh \"$1\" '\\''echo \"$PATH\"'\\' cthulhu
Clearly, it's better to use a script, or at worst the function approach until you need more complicated commands that you can comfortably quote.