5

See the code below:

a()(alias x=echo\ hi;type x;alias;x);a

I have an alias inside a function, I do not want to change the external environment (that is why I am using () instead of {}), even the code saying the alias was successfully setted, it does not work, check the output out:

x is aliased to `echo hi'
...
alias x='echo hi'
x: command not found

I heard about doing shopt -s expand_aliases would solve, but not only it has not had any effect as well as I could not depend on bash because I am working with dd-wrt's busybox's ash.

Someone know this issue?

4
  • 1
    Why are you using an alias in there, anyway?
    – muru
    Commented Jan 23, 2015 at 17:49
  • @muru in dd-wrt I need to save bytes, so I use alias a lot, because it is shorter than a function and a variable: beginning with alias a=alias I can do a e=echo vs e()(echo "$@"), and it is better than e=echo because it needs to be called as $e args vs just e args. Commented Jan 23, 2015 at 18:01
  • I guess eval defeats the purpose then.
    – muru
    Commented Jan 23, 2015 at 18:05
  • @muru - not necessarily. If you eval the alias assignment you might come off with it - depending on shell/context. Another simple thing could be to define a fn within the function and use it w/ parameters/eval to define/call aliases at will.
    – mikeserv
    Commented Jan 23, 2015 at 21:14

2 Answers 2

5

I don't use dash, but here is what bash manual has to say about aliases:

The rules concerning the definition and use of aliases are somewhat confusing. Bash always reads at least one complete line of input before executing any of the commands on that line. Aliases are expanded when a command is read, not when it is executed. Therefore, an alias definition appearing on the same line as another command does not take effect until the next line of input is read. The commands following the alias definition on that line are not affected by the new alias. This behavior is also an issue when functions are executed. Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a compound command. As a consequence, aliases defined in a function are not available until after that function is executed. To be safe, always put alias definitions on a separate line, and do not use alias in compound commands.

And another quote, this time from zsh manual:

There is a commonly encountered problem with aliases illustrated by the following code:

          alias echobar='echo bar'; echobar

This prints a message that the command echobar could not be found. This happens because aliases are expanded when the code is read in; the entire line is read in one go, so that when echobar is executed it is too late to expand the newly defined alias. This is often a problem in shell scripts, functions, and code executed with source or .. Consequently, use of functions rather than aliases is recommended in non-interactive code.

I'm pretty sure it is similar in other shells as well.

3
  • No, I don't think that's it. You can test it out by inserting as many new lines as you want, but it still says ash: x: not found.
    – muru
    Commented Jan 23, 2015 at 20:17
  • Ah, damn. You're right. That bit about aliases not being available until after the function is executed. Which means re-executing this function won't be of use, given it is using a sub shell.
    – muru
    Commented Jan 23, 2015 at 20:18
  • "Aliases are expanded when a command is read, not when it is executed." I think this is happening when the script or input is initially parsed. So the alias inside the function is expanded (or not expanded) before any commands are executed (before the alias is actually defined at runtime). Commented Apr 17, 2020 at 0:09
3

If you're not averse to using eval:

$ busybox ash -c 'a()(alias x=echo\ hi;type x;alias;eval x);a'
x is an alias for echo hi
x='echo hi'
hi

I have no idea why this works.

2
  • 1
    This is the answer and the reason is eval gives the parser another pass - the function is a literal string of text stored in memory executed at once as a substitution - it doesnt get the same treatment as an input file does in which the parser gets to both evaluate the assignment in one command context and its execution in another - it just runs. Introducing eval gives the shell a chance to check the string it has stored in memory a second time over.
    – mikeserv
    Commented Jan 23, 2015 at 21:10
  • It works, although I will try an alternative way to do the same Commented Jan 23, 2015 at 23:08

Not the answer you're looking for? Browse other questions tagged .