#758542 dash: defining an alias with a backslash should return an error

Package:
dash
Source:
dash
Description:
POSIX-compliant shell
Submitter:
Vincent Lefevre
Date:
2023-01-08 13:42:03 UTC
Severity:
wishlist
Tags:
#758542#5
Date:
2014-08-18 17:11:13 UTC
From:
To:
With dash, one can defined an alias with a backslash:

$ alias '\mv=echo ab'
$ alias
\mv='echo ab'

but it can't be used:

$ \mv
mv: missing file operand
Try 'mv --help' for more information.

So, it would be better to return an error like with bash and ksh93.

#758542#10
Date:
2022-12-17 19:11:55 UTC
From:
To:
Indeed; escaping of any kind (quoting or backslash) disables alias
expansion. What's even more fun is that if you do
  alias "a'b=c" "ls&id=cd"
then alias will give you
  ls&id='cd'
  a'b='c'
which is even better since it'll run code when evaled back.

I posted a patch to dash@ (archived at forwarded-to) that fixes this
by refusing characters that must be quoted in the name bit of the alias,
so the invocation above will fail with
  alias: a'b: invalid name
  alias: ls&id: invalid name
and commit no aliases.

наб

#758542#19
Date:
2022-12-19 21:58:44 UTC
From:
To:
Rejected by upstream, citing minimalness.

A related fix for
  eval "alias $(alias 'a|b|c=d'; alias)"
executing code was accepted
(well "will look into it"ed, where "it" is my complete patch? idk), tho.

This makes dash, as you note, incompatible with ksh93 and bash,
but compatible with zsh in this regard. Seeing as this is an extension
to POSIX (which requires that the name matches [a-zA-Z0-9_!%,@]+),
both approaches are valid.

Tentatively tagging as wontfix, maybe Andrej will want to pull that in
as a patch, but upstream has spoken for the time being.

наб

#758542#26
Date:
2022-12-19 22:29:36 UTC
From:
To:
No, it is still incompatible with zsh, where one can use the alias:

zira% alias '\mv=echo ab'
zira% alias
'\mv'='echo ab'
run-help=man
which-command=whence
zira% \mv
ab
zira%

But perhaps if an alias with a backslash is accepted like in zsh,
then the fact that it is not used by dash is a bug. This doesn't
match the dash man page:

   Aliases
     An alias is a name and corresponding value set using the alias(1)
     builtin command.  Whenever a reserved word may occur (see above),
     and after checking for reserved words, the shell checks the word
     to see if it matches an alias.  If it does, it replaces it in the
     input stream with its value.  [...]

and about the backslash:

   Backslash
     A backslash preserves the literal meaning of the following character,
     with the exception of ⟨newline⟩.  A backslash preceding a ⟨newline⟩
     is treated as a line continuation.

But backslash handling is normally done *after* checking for aliases.
This typically allows one to disable an alias by using a backslash:

$ alias foo="echo ab"
$ foo
ab
$ \foo
dash: 3: foo: not found
$ f\oo
dash: 4: foo: not found

#758542#31
Date:
2022-12-19 22:46:48 UTC
From:
To:
Hi!

I meant the compatibility insofar as the alias name domain
(and, roughly, the output when the name needs quoting).

I didn't even try the expansion behaviours, because none of them should
expand, and zsh is wrong to do this (quoting my patch from forwarded-to):
+ * POSIX Issue 7, XCU, 2.3.1 Alias Substitution:
+ *   the command name word of a simple command shall be examined
+ *   to determine whether it is an unquoted, valid alias name
+ * POSIX Issue 7, XCU, 2.2 Quoting:
+ *   The various quoting mechanisms are the escape character,
+ *   single-quotes, and double-quotes.

As in one /can/ accept anything in the name (XBD, 3.10 Alias Name),
but /must not/ expand aliases when the word is in any way quoted.

Yeah, you're right here; dash conforms to the standard and doesn't
alias-expand quoted command names, sorry for the confusion.

(Also, the command name in your last prompt is "mv", so it's doubly
 weird that zsh allows it to match "\mv". In sh and ksh compatibility
 mode, which zsh apparently has, zsh does behave like dash/POSIX
 (allows anything in the name, doesn't expand aliases when quoted)).

Best,
наб

#758542#36
Date:
2022-12-20 00:09:47 UTC
From:
To:
I would say that "\mv" is a quoted version of "mv", but an unquoted
version of "\mv".

I suspect that the POSIX text is based on the fact than normally,
alias names cannot contain quote mechanisms. Otherwise it is rather
ambiguous.

Note also that

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/alias.html

says: "An alias definition provides a string value that shall replace
a command name when it is encountered", with no restrictions on words
with quoting mechanism.

No, alias substitution is done before quoting mechanisms are involved.
Otherwise one would get an error in the following:

$ alias 'foo=\echo ab'
$ alias
foo='\echo ab'
$ foo
ab

However, emulation is not perfect. For instance:

zira% emulate ksh
zira% alias '\mv=echo ab'
zira% ksh93
$ alias '\mv=echo ab'
ksh93: alias: \mv=echo ab: invalid alias name

I also suspect that if aliases with backslashes have been defined
before zsh is put in sh/ksh compatibility mode, like that:

zira% alias '\mv=echo ab'
zira% emulate sh
zira% alias
'\mv'='echo ab'
run-help=man
which-command=whence

then such aliases must be ignored in sh/ksh compatibility mode to
avoid some surprises. This can explains the zsh behavior in alias
substitution.

However, in sh/ksh compatibility mode, I would rather expect

  alias '\mv=echo ab'

to give an error, as the alias name is invalid in this context.

BTW, the zsh extension makes sense if one want to be able to alias
some forms of commands with special characters. For instance:

zira% alias '\\foo=echo ab'
zira% \\foo
ab
zira% ""\\foo
zsh: command not found: \foo

#758542#41
Date:
2022-12-20 01:44:32 UTC
From:
To:
Hi!
Well, when turning tokens into words, quotes are removed (XCU, 2.6), so.
Not really. The only bit affected is that XCU, alias, STDOUT only
requires that the value be quoted for reinput to the shell, and not the
name; naturally if you accept more than the minimum
(XBD, 3.10 Alias Name: [a-zA-Z!^,@]+),
then you should quote the name to preserve this property as well.
Yes, and it includes a link to the shell tokenisation rules,
which are actually normative w.r.t. when alias substitution happens:
  After a token has been categorized as type TOKEN (see Section 2.10.1), including (recursively)
  any token resulting from an alias substitution, the TOKEN shall be subject to alias substitution
  if:
    * the TOKEN does not contain any quoting characters,
    * the TOKEN is a valid alias name (see XBD Section 3.10),
    * an alias with that name is in effect,
    * the TOKEN did not either fully or, optionally, partially result from an alias substitution of
      the same alias name at any earlier recursion level, and
    * the TOKEN could be parsed as the command name word of a simple command (see
      Section 2.10), based on this TOKEN and the tokens (if any) that preceded it, but ignoring
      whether any subsequent characters would allow that,
(Issue 8 Draft 2.1, cf. https://www.austingroupbugs.net/view.php?id=953;
 Issue 7 says the same thing about the quoting bits but less eloquently.)
No? Again, XCU, 2.3.1 Alias Substitution:
  When a TOKEN is subject to alias substitution, the value of the alias shall be processed as if it
  had been read from the input instead of the TOKEN, with token recognition (see Section 2.3)
  resuming at the start of the alias value. When the end of the alias value is reached, the shell may
  behave as if an additional <space> character had been read from the input after the TOKEN that
  was replaced. If it does not add this <space>, it is unspecified whether the current token is
  delimited before token recognition is applied to the character (if any) that followed the TOKEN
  in the input.

"foo\n" is read, partially tokenised as as [<foo>, ...], this satisfies
the conditions above, tokenisation restarts on "\\echo ab\n",
this yields [<\echo>, ...], this contains a quote, so tokenisation
continues for [<\echo>, <ab>]; <\echo> isn't a reserved token, so
XCU 2.10.2 pt. 7a doesn't apply so pt. 1 sentence 2 applies and
word expansion occurs, yielding a simple_command consisting of
cmd_name=(echo) cmd_suffix=(ab)
(i ran out of markup for WORDs, parens must do).
"echo" is executed with single parameter "ab".
(Roughly; the syntax is mind-numbing, and there's so many steps that do
 nothing here. You get the point.)
Well, I wouldn't say "ignored". The Zsh Language just applies
a different set of rules to alias expansion (effectively, it provides
aliases for TOKENs instead of WORDs; this is weird and confusing,
since you /never/ observe TOKENs directly like this and you /execute/
WORDs; frankly, the decision to not do quote removal in this context is
/baffling/, but, y'know. zsh.). Under sh emulation it applies the correct
rules. In both languages all characters are valid parts of an alias.
Why? Again, XBD, 3.10 Alias Name:
  Implementations may allow other characters within alias names as an extension.
You directly observe that a backslash is a valid part of an alias name
because alias didn't exit >0. It's just fundamentally impossible for it
to ever actually get substituted. This matches dash.

Many characters aren't valid parts of the alias name under ksh and bash
(and mksh), even those that would be fine to substitute (under bash
that's the slash). That's fine, too.

Best,
наб

#758542#46
Date:
2023-01-08 13:38:47 UTC
From:
To:
Control: clone -1 -2
Control: retitle -2 dash: accepts anything as an alias name and doesn't quote it, then eval $(alias) will run alias as code
Control: tags -2 - wontfix
Control: tags -2 + fixed-upstream
This new-found bug (-2 if i got my headers right) has been fixed in:
https://git.kernel.org/pub/scm/utils/dash/dash.git/commit/?id=4ec545e8dc98a3f461cf56bed03adafa81c64aec
by just quoting the alias names in addition to the values.

The original bug remains upstream wontfix.

наб