Tuesday, July 27, 2010

LINUX: Scripts and Alias

This one stumped me for a while; we had build scripts that kept failing, yet if you manually executed the commands from the script, everything worked fine. Eventually I discovered that it wasn't properly executing the "make" command as was setup in the alias. When we set up our build environment, we alias "make" to have a bunch of compiler flags so when you finish running the setupenv file (to set environment variables and such), all you have to do is run "make" and the alias keeps track of all the necessary build flags.

Scripts never work properly because they interpret your "make" command to be just that--"make" with no build flags. It turns out this is because scripts (non-interactive shells) (1) don't expand aliases and (2) don't bring in the current environment.

Concerning point 2 about the current environment: This is actually a good thing. Imagine if you have a special environment you set up and the script runs fine in your area, then someone else tries to run it in theirs and it fails. It would be a pain to begin debugging by comparing what is in your .bashrc file and what you did to customize your environment. A script should be self contained, able to run in any environment. So when you run it, it doesnt carry over your environment variables into its non-interactive subshell that it runs in.

We are more concerned with point 1 though: A shell script, at least on all the Fedora and RedHat servers I've worked on, do not expand the aliases by default. Before I came across the good solution on the internet, I hacked together my own solution of parsing the expanded command out of the alias output:
`alias | grep make | cut -d\' -f2`
That works, but is not an elegant solution by any means. Recently a coworker was having trouble with a build script and I got into explaining to him he may have a problem with a non-expanding alias. I showed him my solution and then gave the caveat that there is probably a better, easier solution out there. Then I looked for one and found it (don't ask me why I didn't find it the first time when I created my hack solution). Just place the following command in the begginning of your shell script:
shopt -s expand_aliases
What this does is sets the shell option (shopt) to enable (-s) the expansion of aliases (expand_aliases). The man page says that this option is enabled by default for interactive shells, so the implication (and reality we've witnessed) is that it is disabled by default for non-interactive shells.

If you want to see this in action, try this script:
#!/bin/bash
alias testme="echo It Worked"
alias
testme
The result will print the new alias you assigned (typing alias in line 3 prints out current aliases) for testme. Notice there are no other aliases set, not even the ones that are set globally for the system. The execution of the testme command fails with "command not found" because it is not recognizing the alias.

Now try again with the shopt set:
#!/bin/bash
shopt -s expand_aliases
alias testme="echo It Worked"
alias
testme
Now it will work perfectly, showing the alias we set and echoing the "It Worked" statement.

2 comments:

  1. Hi,

    I tried your example with an alias that I had defined in my .bash_aliases file..while testme is substituted properly the aliases from .bash_aliases don't seem to be.

    Is that how it is supposed to be?

    Is there any way that I can take advantage of my predefined aliases in my shell scripts?

    ReplyDelete
  2. Hello,

    Would you happen to know how we can do the same thing with zsh and ksh (not bash)?

    Regards,

    ReplyDelete