23

From the bash manual

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.

The two sentences "Aliases are expanded when a function definition is read, not when the function is executed" and "aliases defined in a function are not available until after that function is executed" seem to be contrary to each other.

Can you explain what they mean respectively?

2
  • Good question! I leaned something new today. I learned 2 things, actually: 1) this info about alias expansion at the time the function def is read, and 2) that I should really read the bash manual carefully (which I thought I already did, but apparently didn't ^^ ) Dec 13, 2016 at 10:44
  • 4
    The main question is, should anyone use aliases in noninteractive mode? That's what functions are for, and aliases just make scripting more error-prone. I've actually never needed the info above, because I only ever encountered aliases in .bashrc files at the very top.
    – orion
    Dec 13, 2016 at 11:04

3 Answers 3

40
  1. Aliases are expanded when a function definition is read, not when the function is executed …

    $ echo "The quick brown fox jumps over the lazy dog." > myfile
     
    $ alias myalias=cat
     
    $ myfunc() {
    >     myalias myfile
    > }
     
    $ myfunc
    The quick brown fox jumps over the lazy dog.
     
    $ alias myalias="ls -l"
     
    $ myalias myfile
    -rw-r--r-- 1 myusername mygroup 45 Dec 13 07:07 myfile
     
    $ myfunc
    The quick brown fox jumps over the lazy dog.

    Even though myfunc was defined to call myalias, and I’ve redefined myalias, myfunc still executes the original definition of myalias.  Because the alias was expanded when the function was defined.  In fact, the shell no longer remembers that myfunc calls myalias; it knows only that myfunc calls cat:

    $ type myfunc
    myfunc is a function
    myfunc ()
    {
    cat myfile
    }
  2. … aliases defined in a function are not available until after that function is executed.

    $ echo "The quick brown fox jumps over the lazy dog." > myfile
     
    $ myfunc() {
    >     alias myalias=cat
    > }
     
    $ myalias myfile
    -bash: myalias: command not found
     
    $ myfunc
     
    $ myalias myfile
    The quick brown fox jumps over the lazy dog.

    The myalias alias isn’t available until the myfunc function has been executed.  (I believe it would be rather odd if defining the function that defines the alias was enough to cause the alias to be defined.)

4
  • 5
    +1, good answer. An important "corollary" to this is that if one intends to define functions and aliases in a script, better put the aliases definition before the functions definitions! (obvious, now, given the answer, but I didn't knew this). Dec 13, 2016 at 10:47
  • Thanks. I think an underlying issue is the difference between running the definition of a function and calling a function. Specifically, what shell operations are performed during running the definition of a function and during calling the function respectively? Is there any shell operations which are performed during both running the definition of a function and calling the function, or do running the definition of a function and calling the function perform non-overlapping set of shell operations?
    – Tim
    Aug 5, 2017 at 17:18
  • Well, it’s a little like the difference between building a car and driving a car.  Or buying a sandwich and eating a sandwich.  I have provided a more detailed answer to your other question. Aug 5, 2017 at 21:40
  • Thanks. After rereading the quote from the bash manual and your reply, I am confused about the meaning of executing a function. Does it mean executing the definition of a function, or calling a function? See unix.stackexchange.com/q/384209/674
    – Tim
    Aug 5, 2017 at 23:20
3

I need the answer that is stated by the first sentence when I try below snippet in my .bashrc.

alias ls='\ls -F --color=auto --show-control-chars'
alias ll='ls -ahl'
function lf_macro() {
    local CMD=${1:-ls} DIR=${2:-.};
    $CMD $(find $DIR -maxdepth 1 -type f);
}
function lf() { lf_macro ll "$1"; }
function lsf() { lf_macro ls "$1"; }     # list all file, no directories

after unalias -a; source ~/.bashrc, I try to execute lf and lsf,

$ lf
-bash: ll: command not found

$ lsf
./file1 ./file2 ./script.sh ...     # no color, no control-chars

$ ls $(find -maxdepth 1 -type f)
./file1 ./file2 ./script.sh* ...

it seems clearly that aliases are expanded at function definition, not function execution, since:

  • when I execute lf, the error -bash: ll: command not found, and
  • when I execute lsf, /usr/bin/ls is used, not the alias form, no color highlight, and no control chars after executable file.
-4

You can say (for example)

alias mvn="mvn -Dmaven.wagon.http.ssl.insecure=true $@"

IE you can repeat the name of the alias within the definition, but you can't do that with a function; bash will just stack overflow and die.

There's probably a way around this but IDK what it is.

2
  • Note that $@ would be expanded at the time the alias is defined as it's in double quotes. However, I'm assuming you are trying to use $@ as "the arguments of the alias". An alias does not take arguments, it just replaces the command with a string, and any further arguments are just tucked on to the end of the resulting command line. Regarding your issue with calling mvn from within a function called mvn, just use command mvn to call the "real" mvn in your function (this would avoid calling the function recursively).
    – Kusalananda
    Jan 6 at 18:27
  • 1
    Also note that you are not actually answering the question that Tim posed.
    – Kusalananda
    Jan 6 at 18:28

You must log in to answer this question.

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