• Uncategorized

About bash : Execute-a-passed-alias-inside-a-function

Question Detail

Background:

I’m trying make a function that runs commands on a set interval because I don’t have access to a “watch” program. Simplified to it’s most basic from, the function I’m trying to write is runit() { $1; }.

What works:

This works fine and dandy when I pass it things that aren’t aliases. For example, runit "ls -l" works fine. I get the full output from the ls -l command.

What doesn’t work:

The problem starts when I pass it an alias. For example, setting alias ll="ls -l" then calling runit "ll" will result in -bash: ll: command not found.

Things I have tried:

When I hard-code the alias runit() { ll; }, it works fine and gives me what I expect.


I feel like I might be overlooking something, but I can’t quite place my finger on it.
Why would hard-coding the alias work fine, but passing it into the function fail?
Is there a way to accomplish what I’m attempting to do?

Question Answer

From the bash man page discussion of aliases (emphases mine):

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.

You can observe this effect in functions by using the type command:

$ run_it () { ll; }
$ type run_it

You should see that the body of the function contains a call to ls -l, not ll.

The last sentence of the section on aliases:

For almost every purpose, aliases are superseded by shell functions.

My interpretation of that line is: if you think you want to use an alias, try writing a function first. Don’t use an alias unless the function demonstrably fails to do what you need.

You can use eval like this:

$ runit() { eval $1; }
$ alias ll="ls -l"
$ runit "ll"

eval will expand any alias in $1 before the execution.

One way to solve this problem is to define a shell function rather than an alias.

ll () {
  ls -l "[email protected]"
}

The alias is expanded as a macro on command input, whereas the shell function is matched when the command is executed. This is a perfect example of how the shell’s macro processor language is good for interactive grace but rather complicates actual programming.

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.