Bash Alias – Precedence and Shadowing Issues

bash

I have some aliases setup (in this order) in .bashrc:

alias ls="lsc"
alias lsc='ls -Flatr --color=always'
alias lscR='ls -FlatrR --color=always'

Confirming them with alias after sourcing:

alias ls='lsc'
alias lsc='ls -Flatr --color=always'
alias lscR='ls -FlatrR --color=always'

I can run the newly aliased ls just fine, and it chains through to the lsc alias, and then executes the command associated with the lsc alias. I can also run lscR and it operates as expected.

If I try to run lsc itself though, I get:

$ lsc
lsc: command not found

Any idea why the shell seems to be shadowing/hiding the lsc alias in this scenario?
(I realise it's pointless to run 'lsc' when I can just run 'ls' to get the same result here, but I'm trying to understand the shells behaviour in this scenario).

EDIT: Workarounds below for the (bash) shell behaviour provided in the question answers.

Some really helpful answers have been provided to the original question. In order to short-circuit the expansion behaviour that is explained in the answers, there seems to be at least two ways of preventing a second alias, from trying to expand a command that you have already aliased. For example, if you have alias cmd='cmd --stuff' which is overriding a native command called cmd, you can prevent the 'cmd' alias from being used in place of the native cmd within other aliases, by:

(thanks to wjandrea's comment for this first approach)

  1. prefixing cmd with 'command' in the other alias e.g. alias other-cmd-alias='command cmd --other-stuff'

or,

  1. Similarly, you can escape aliases (as you can also do on the command line), within other aliases by prefixing with a backslash '', e.g. alias other-cmd-alias='\cmd --other-stuff'.

Best Answer

Bash does allow aliases to contain aliases but it has built-in protections against infinite loops. In your case, when you type lsc, bash first expands the alias to:

ls -Flatr --color=always

Since ls is also an alias, bash expands it to:

lsc -Flatr --color=always

lsc is an alias but, quite sensibly, bash refuses to expand it a second time. If there was a program named lsc, bash would run it. But, there is not and that is why you get command not found.

Addendum

It is different when lscR runs. lscR expands to:

ls -FlatrR --color=always

Since ls is an alias, this expands to:

lsc -FlatrR --color=always

Since lsc is an alias, this expands to:

ls -Flatr --color=always -FlatrR --color=always

Since ls has already been expanded once, bash refuses to expand it a second time. Since a real command called ls exists, it is run.

History

As noted by Schily in the comments, bash borrowed the concept of not expanding an alias a second time from ksh.

Aside

Aliases are useful but not very powerful. If you are tempted to do something complex with an alias, such as argument substitution, don't; use a shell function instead.

Related Question