Bash – Why doesn’t alias work inside if

aliasbash

My .bashrc sets up a bunch of aliases for me to use as needed, and then runs one of them automatically.

Turns out this caused some problems with automated scripts ssh-ing into my machine, as opposed to using an interactive shell. So, to fix this, I put them inside an if block so they wouldn't be defined or run for those automated scripts…

if [ -n "$TERM" ] && [ "$TERM" != "dumb" ] ; then
    alias short='long command here'
    alias another='very long command here'
    # ...

    short
fi

Only to see short: command not found!

Let's reduce this to the minimum…

$ cat alias.sh
alias foo='echo hi'
foo
$ sh alias.sh
hi
cat alias-in-if.sh
if true ; then
        alias foo='echo hi'
        foo
fi
sh alias-in-if.sh
alias-in-if.sh: line 3: foo: command not found

Why does the first script work, and not the second?

(I've answered my own question.)

Best Answer

I searched around and didn't find anything to do with aliases inside ifs, only aliases inside functions, which seemed a different issue at first.

But actually, this answer solves it:

Bash always reads at least one complete line of input before executing any of the commands on that line. [...] The commands following the alias definition on that line are not affected by the new alias. [...] To be safe, always put alias definitions on a separate line, and do not use alias in compound commands.

Seems this isn't just limited to functions, but if statements as well. The entire if block is treated as a single line. The solution, to ensure the alias is only defined and executed if the condition is true, yet ensure it isn't even defined if the condition is false, is to evaluate the condition twice like this:

cat alias-before-if.sh
if true ; then
        alias foo='echo hi'
fi

if true; then
        foo
fi
$ sh alias-before-if.sh
hi

So my original script would be:

if [ -n "$TERM" ] && [ "$TERM" != "dumb" ] ; then
    alias short='long command here'
    alias another='very long command here'
    # ...
fi

if [ -n "$TERM" ] && [ "$TERM" != "dumb" ] ; then
    short
fi
Related Question