25

I see this pattern every once in a while, especially in questions about Bash prompt customization.

alias f='_ () { useful code; }; _'

I can see no reason at all to create an alias here. The obvious refactoring

f () { useful code; }

which avoids declaring an alias altogether, and simply defines the function once and for all, seems simpler, more understandable, less brittle, and more efficient. (In case it's not obvious, the alias ends up redeclaring the function every time you invoke the alias.)

For example, Make a Bash alias that takes a parameter? has several answers which exhibit this technique. bash script to run lftp with ftp path is a question which has code like this in a question about the actual functionality inside the function, and the OP doesn't explain why even though I prodded gently.

Is this just plainly an antipattern, or is there an actual reason to do this? Under what circumstances would this design make sense?

This is not about aliases with a space after them, or about code obfuscation (the examples I have found are generally entirely readable, apart from this mystifying technique).

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Just a shot in the dark: Is it possible that the `alias` above is generated by a code? Just like many modern websites have `javascript` code _generated_ by a `java` or `dot-net` code. The reason there is _obfuscation_. – anishsane Jul 28 '19 at 12:15
  • 1
    @anishsane Nice hypothesis, but why would you want to obfuscate your own Bash prompt? Also, if you really wanted the code to be hard to read, this isn't a very good obfuscation. – tripleee Jul 28 '19 at 12:31
  • Even the obfuscated `js` code is plain text. But it is still hard to read and trace. Can you show us some actual examples? If it is just the function name being changed to an alias, then it is not for obfuscation. – anishsane Jul 28 '19 at 13:29
  • 1
    Another possibility is, aliases are disabled [(by default)](https://unix.stackexchange.com/a/158040/26926) in a shell script. So, maybe it is some functionality intended to be used only in an interactive shell. – anishsane Jul 28 '19 at 13:32
  • 1
    That could be a possible answer, though the disadvantages far outweigh the actual benefit in my book. – tripleee Jul 28 '19 at 14:03
  • https://stackoverflow.com/questions/7131670/make-a-bash-alias-that-takes-a-parameter has several answers which exhibit this technique (and I think all of them have flabbergasted comments pointing out how this is weird and probably unnecessary). – tripleee Jul 28 '19 at 14:14
  • If the alias ended with a space then it could have been [something like this](https://unix.stackexchange.com/a/25329/187122). – Socowi Jul 28 '19 at 14:45
  • @Socowi Thanks for the speculation, but no, that's a separate issue. Updated question. – tripleee Jul 28 '19 at 15:16
  • IIRC, it's related to run order: aliases have priority over functions. I believe you can solve that by declaring `function f() { …; }` rather than just `f() { …; }` and it'll have the same (higher) priority as `alias f='…'` – Adam Katz Jul 28 '19 at 15:40
  • 1
    @AdamKatz `function` doesn't induce any higher priority. `function f() { echo foo; }; f () { echo bar; }; f` still outputs `bar`, as the second function replaces the first. – chepner Jul 28 '19 at 15:56
  • 1
    @chepner – I'm referring to name collisions. I don't recall the exact scenario in which I discovered that, but iirc it was something like a `alias f='…'` trumping `f() {…}` but an explicit `function f() {…}` would trump the alias. – Adam Katz Jul 28 '19 at 16:01
  • 1
    Ah, the issue there is a weird one: If you define the alias, then the `f` in `f() { ...}` itself is subject to alias expansion; the keyword `function` prevents that by occupying the command position. – chepner Jul 28 '19 at 16:05
  • 2
    In https://stackoverflow.com/a/3322412/3220113 this trick is mentioned for the `alias` inside `git`. A different situation, maybe somebody got the wrong inspiration from it. – Walter A Aug 03 '19 at 22:32
  • 1
    The [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) also uses this pattern. I asked them to clarify in [this issue](https://github.com/kubernetes/website/issues/32275). – EndlosSchleife Mar 15 '22 at 10:38

5 Answers5

14

Here are my 2 cents on this and it represents my personal opinion as well as understanding on the topic.

  • Using aliases with functions is to some extent a personal preference of developers. I will add some differences between the two approaches, which may also account for personal preferences of using aliases vs functions
  • There are times when most of the things I want to do are possible with aliases itself but only a few require to take a parameter. So instead of mixing aliases with functions, I use an alias with the function itself

Example:

alias kgps='kubectl get pods --all-namespaces | grep '

This works great and I can search my kubernetes pods. Now for deleting these pods, I need to pass the same parameter but in between the command, so I use an alias with a function inside

alias kdp="_(){ kubectl get pods --all-namespaces  | grep \$1 | awk '{print \$2}' | xargs kubectl delete pod; }; _"

So most of my shortcut commands are possible to execute through aliases and only few which needs such things I use aliases with functions.

Aliases vs Functions

Now there are few differences between aliases and functions which I would like to highlight

Aliases can override system commands much more easily compared to functions

If I need to override ls, I can do that much easier with alias

alias ls='ls -altrh'

While a function equivalent of the same would be like below

ls() { command ls -altrh "$@";}
ls() { /bin/ls -altrh "$@";}

Aliases intention is mostly for shortcuts

Aliases are majorly used to create shortcut commands while functions are used for a lot of things, complex combinations of commands, auto-completion, bash prompts

Aliases are easier to manage

Run alias command you get a list of currently active aliases

$ alias
....
vs='vagrant ssh'
vu='vagrant up'
vus='vu && vs'
....

To get the list of functions we need to use declare -f or another similar command

$ declare -f | wc -l
  8226
$ alias | wc -l
  217

Now if I post a partial output of declare -f I get

$ declare -f
...
vi_mode_prompt_info () {
    return 1
}
virtualenv_prompt_info () {
    return 1
}
work_in_progress () {
    if $(git log -n 1 2>/dev/null | grep -q -c "\-\-wip\-\-")
    then
        echo "WIP!!"
    fi
}
zle-line-finish () {
    echoti rmkx
}
zle-line-init () {
    echoti smkx
}
zsh_stats () {
    fc -l 1 | awk '{CMD[$2]++;count++;}END { for (a in CMD)print CMD[a] " " CMD[a]/count*100 "% " a;}' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl | head -n20
}

As you can see there are lots of functions which are used but are not relevant to me. While the alias command gives me a very concise output and I can easily see what all is there. In my case, 100% of them are shortcut commands

Escaping aliases and functions syntax is different for system commands

To escape a defined alias you need to prefix it with \ while for functions you need to either use command <originalcommand> or absolute path of the command /bin/originalcommand

Aliases have higher priority over function

Look at the below example

alias ls='echo alias && ls'
$ ls() { /bin/ls -al }
alias
$ ls
alias
total 23173440
drwxrwxr-x+ 255 tarunlalwani  staff        8160 Jul 30 22:39 .
drwxr-xr-x+ 113 tarunlalwani  staff        3616 Jul 30 23:12 ..
...

As you can see when we run the ls command, first the alias is used and then the next ls is calling the function.

This becomes also a way of wrapping an exiting function with the same name and re-using the original function inside as well, which can only be done using alias and promotes the format in the question

Lior Bar-On
  • 10,784
  • 5
  • 34
  • 46
Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
  • 2
    This explains why some people (IMHO misdirectedly) prefer aliases but not really why you would wrap a function inside an alias. – tripleee Jul 30 '19 at 17:42
  • If you have functions you don't use, that's hardly an argument against the functions you do use? Some distros define aliases in my default profile, too (I routinely `unalias ll` etc when I notice, just to keep my environment clean). – tripleee Jul 30 '19 at 17:44
  • 1
    I will dig more and see if I find something else which could explain this. But as of now this is what I have got – Tarun Lalwani Jul 30 '19 at 17:48
  • Actually, this scratches an itch of mine - I have created some custom functions, but listing them is hard compared to the ease of running "alias" to get a list. That might actually be enough benefit for me to start using this syntax for my custom functions. – Richard Neish Aug 02 '19 at 15:25
7

I found this answer too [U&L] In Bash, when to alias, when to script, and when to write a function? which explains the benefit of defining a function in an alias.

The benefit of doing so over declaring a function is that your alias cannot be simply overwritten by source-ing (or using .) a script which happens to declare a same-named function.

kvantour
  • 25,269
  • 4
  • 47
  • 72
Lety
  • 2,511
  • 21
  • 25
6

I found an Ask Ubuntu question about a related topic where one of the answers alleges that this is a misunderstanding of a different design principle: give the function a long and descriptive name, and create a shorter alias for convenience.

This still offers no insight into why you would have the alias redeclare the function every time.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • I'm with you, choose a reasonable, concise and descriptive name to begin with and nix the whole `alias` bit. It's superfluous. – David C. Rankin Jul 28 '19 at 08:30
  • 2
    One point says, aliases have higher precedence than functions. So maybe it makes some difference? Just a guess, but would be interesetd to know the complete explanation. – Mihir Luthra Jul 28 '19 at 08:32
  • It's easy to say "aliases have higher priority than functions" (since alias expansion occurs prior to command lookup, but that's doesn't explain *why* you would want or need such override behavior. – chepner Jul 28 '19 at 15:59
  • @chepner When you want to override an existing complex function with a temporary alias, you can restore the original function with `unalias`. – Walter A Aug 03 '19 at 21:58
2

You can use the alias for turning on and off a function that you don't want to change. Suppose you have have code that calls the function _. You can switch the implementation of the function for another one with

alias f='_ () { echo "useful code"; }; _'
alias g='_ () { echo "Other useful code"; }; _'
alias h='_ () { echo "Special code"; }; _'

And now you can call

f
_
g
_
h
_
f

@DavidC.Rankin commented correctly, that it looked terrible.
I agree.
I thought of some way to use it. You might use it for testing software, something like

alias ok='commitTransaction () { echo "commited"; return 0; }'
alias nok='commitTransaction () { echo "unknown error"; return 1; }'
alias locked='commitTransaction () { echo "locked"; return 2; }'
alias slow='commitTransaction () { sleep 20; echo "commited"; return 0;  }'

And now the tester can run his testcases:

ok
# And now start ok test
nok
# And now start nok test

Still hacking, why not make a better teststub?

Walter A
  • 19,067
  • 2
  • 23
  • 43
  • This can't be the reason people do this in isolation. I'll update the question with a link to actual real-life examples. – tripleee Jul 29 '19 at 05:08
  • @tripleee I agree. The link in the updated question shows that it might have been constructed as an answer to "How to add a parameter to an alias" where using the `alias` was a "requirement". – Walter A Jul 29 '19 at 06:42
  • That specific question is a bad example in that regard; but as mentioned in the question already, I see this in a lot of places where aliases are not central to the use case. Git customizations for the Bash prompt is one recurring example. – tripleee Jul 29 '19 at 07:00
2

Is this just plainly an antipattern (sic)...

I think its prevalence may just be cargo cult programming. Aliases are easy to understand, so users often learn them first. As users' skill and needs increase, they discover aliases lack flexible argument processing. So they do a web search, like "shell alias parameter passing", and find posts suggesting the pattern:

alias foo='_() { echo $2 $3 $1; }; _'

Lo and behold, it works. Users are happy, and they move on.

But because the _() sequence looks a lot like a shell incantation (2>&1, >>, etc.), users never think that _() is just compact syntax for function _ and never go to the next step of learning functions. With this alias pattern, they get all the benefit of functions and don't have to learn "new" syntax. Most probably never even notice the nasty side effect: overwriting any prior functions named _.

I searched through Usenet from 1981 to 1991, but I didn't find any direct evidence of this theory, however.

... or is there an actual reason to do this? Under what circumstances would this design make sense?

In the five days I drafted this answer, every reason I conjured came back to an argument against it. The selected answer - that aliases can't be masked in subshells - is a solid reason, though I've never thought to be that paranoid: I don't go around sourc'ing code I've not fully vetted.

tripleee
  • 175,061
  • 34
  • 275
  • 318
bishop
  • 37,830
  • 11
  • 104
  • 139