Why are aliases skipped if escaped?

Unix & Linux Asked on August 2, 2020

A common way to skip aliases is to add a backslash before the aliased command.
For example,

$ alias ls='ls -l'
$ ls file
-rw-r--r-- 1 user user 70 Jul 30 14:37 file
$ ls file

My research has led me to discover other exotic ways to skip aliases, such as
ls, "ls", l""s, ls''.

But where is that behavior documented, or (put another way) why is that the correct behavior? The POSIX specification does not seem to
document any special case which deactivates aliases when preceding them
with a backslash. I also could not find it in my shell manual.

This is a self-answered question.

2 Answers


The bash manual does document it. My emphasis:

The first word of each simple command, if unquoted, is checked to see if it has an alias.

In the mksh manual:

The alias expansion process stops (…) when a quoted word is found

In the zsh manual:

Alias expansion is done on the shell input before any other expansion except history expansion. Therefore, if an alias is defined for the word foo, alias expansion may be avoided by quoting part of the word, e.g. foo. Any form of quoting works, although there is nothing to prevent an alias being defined for the quoted form such as foo as well.

(Nothing, except setopt posix_aliases.)

That's better than the dash manual and the ksh manual. The dash manual describes quoting before reserved words and aliases and doesn't describe the interaction between them. The ksh manual states that reserved words are only recognized when not quoted, but doesn't state this for aliases.

You've found how POSIX specifies it.


Historically, I think this arose partly as a language design choice and partly to simplify implementation. The Bourne shell didn't have aliases, but it did have the behavior that reserved words are only recognized when not quoted. This makes sense because the parser needs to know whether a word is a reserved word early on. In particular, it would be problematic to treat the result of a variable or command substitution as a reserved word. Consider:

if condition

If condition is false and the value of else_or_not_else happens to be else, should command2 be executed? That would require the parser to expand $else_or_not_else — but this part of the code is skipped since condition is false! If `"else" had to be recognized as a keyword, the shell parser would need to have a mode where it processes quoting but not substitutions, which would add another layer of complexity.

Recognizing aliases coming from substitutions would also be problematic. An alias could be something like

alias a='command1; $command2'

and the ; in the alias is a command separator which the parser needs to take into account. If alias expansion happened after variable substitution, the parser would need to be invoked again after variable substitution. In addition, variable substitution would need to be invoked a second time on the expansion of the alias. So it simplifies both the language design and the interpreter implementation to not recognize aliases coming from substitution. And once again, it's easier to keep quoting and substitution together, both for users to understand the language and for implementers to make an interpreter.

Keeping the same rules for reserved words and aliases is also a simplification factor.

Answered by Gilles 'SO- stop being evil' on August 2, 2020

The key is that the shell performs Alias Substitution (section 2.3.1) before applying Shell Grammar rules (section 2.10). The first step is Token Recognition (section 2.3):

2.3 Token Recognition
The token shall be from the current position in the input until a token is delimited according to one of the rules below; the characters forming the token are exactly those in the input, including any quoting characters.

(...Rules of delimiting tokens...)

Once a token is delimited, it is categorized as required by the grammar in Shell Grammar.

2.3.1 Alias Substitution
After a token has been delimited, but before applying the grammatical rules in Shell Grammar, a resulting word that is identified to be the command name word of a simple command shall be examined to determine whether it is an unquoted, valid alias name.

From the bold parts, we conclude that upon reaching the alias lookup step, the quoting has not been removed. (Escaping with a backslash is also quoting.)

Then, issuing ls or ls means that the shell will look for an alias with that exact literal name, i.e., ls or ls. Such alias names are not even possible, so the alias expansion does not happen and the shell passes on to the Grammar Rules, at which point both are resolved to our old-friend ls, since "a <backslash> that is not quoted shall preserve the literal value of the following character". If the PATH shell variable is the ordinary one, /bin/ls is executed without the -l option.

That also explains "ls", 'ls', l""s, l''s, ... skip the alias.

Answered by Quasímodo on August 2, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP