Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I use a script (which I do not have write access to) that creates a bunch of aliases to set up an environment. I would like to create a bash function to setup my environment, but it seems that the aliases do not survive to the function body.

Here's a minimal example:

# aliases.sh
alias fooAlias='echo "this will never work!"'  

.

# .bashrc
function setupLotsOfThings() {
    source aliases.sh
    fooAlias
}

.

Now, if I simply source aliases.sh interactively, things work as expected:

[mycomputer]~/ $ source aliases.sh
[mycomputer]~/ $ fooAlias
this will never work!

However, if I instead call the function defined in my .bashrc, it doesn't recognize the alias after sourcing its definition:

[mycomputer]~/ $ setupLotsOfThings
-bash: fooAlias: command not found

What is going on here? Is there something I am missing about the scope of the alias command when used in a function?

Edit: I'll add some details beyond the minimal example to shine some light on what I'm trying to accomplish.

For my work I develop and run a lot of software on a cluster and/or grid. I have several projects that require completely different environments, such as different gcc versions, specific software releases, config and data PATHs, and various environment variables. Administrators provide the scripts to set up various things, usually by defining shell functions or aliases, which invoke other functions or aliases or run various scripts. To me, it's a black box.

I'd like to setup my own various environments with a single command. Currently, I do something like:

[mycomputer]~/ $ source /some/environment/setup/script.sh
[mycomputer]~/ $ aliasToSetupSomeSoftwareVersion    #this was defined in the above
[mycomputer]~/ $ anotherAliasForOtherSoftware
[mycomputer]~/ $ source /maybe/theres/another/script.sh
[mycomputer]~/ $ runSomeOtherSetup      # this was defined in the new script

These commands generally have to be run in order. My idea basically was to just copy the above lines into a function block, but as original example shows, that simply doesn't work. Alternative workarounds are more than welcome!

share|improve this question
add comment

2 Answers

up vote 2 down vote accepted

An alternative solution is to paste those commands into a text file instead of a function block. Something like:

## This is needed to make the sourced aliases available
## within the script.
shopt -s expand_aliases

source /some/environment/setup/script.sh
aliasToSetupSomeSoftwareVersion
anotherAliasForOtherSoftware
source /maybe/theres/another/script.sh
runSomeOtherSetup

Save that as setup1.sh wherever you like. The trick is to then source this file, not execute it:

$ source setup1.sh

That will run the aliases that are in the script and also make them available to your current shell.

You can further simplify the process by adding this to your .bashrc:

alias setupLotsOfThings="source setup1.sh"

Now you can simply run setupLotsOfThings and get the behavior you wanted from the function.


Explanation

There are two issues here. First, aliases are not available to the function they are declared in but only once that function has exited and second that aliases are not available within scripts. Both are explained in the same section of man bash:

Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt (see the description of shopt under SHELL BUILTIN COMMANDS below).

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 com‐ pound commands.

Then, there is the difference between executing and sourcing a file. Basically, running a script makes it run in a separate shell while sourcing it makes it run in the current shell. So, sourcing setup.sh makes the aliases available to the parent shell while executing it as a script would not.

share|improve this answer
    
Well, I'm working on a cluster, and have several such "aliasing" scripts available that help setup software environments, etc. The alias script defines many different aliases; my goal is to invoke a specific environment by sourcing the correct aliases, and then running (some of) those aliases. I'd rather do this in a single command, than have to run several commands in sequence. –  chase Jan 29 at 4:00
    
And P.S. the script that sets up the aliases is unfortunately very verbose and a bit slow (as it touches many files on the NFS), so I prefer not to source all these things e.g. at login time. –  chase Jan 29 at 4:03
    
@chase but they're only sourced when you run setupLotsOfThings, they're just not available to the function itself. They work o the shell you called the function from. Anyway, if your function only sources aliases why not just use an alias? For example: alias setupstuff="source aliases.sh". –  terdon Jan 29 at 4:07
    
right, but I'm not worried about the scope per se. Ideally I just want to combine the "source stuff" and the "run aliases" step into one. Perhaps it can be done with 3 functions? function sourceStuff() { source ... }; function runStuff() { someAlias; ... }; function setupLotsOfThings() { sourceStuff; runStuff; }; –  chase Jan 29 at 4:11
    
@chase yeah, I thought of that but it didn't work :). Could you expand your question with some more of what you need to do? Functions can only deal with so much complexity, you might have to write a little script instead. –  terdon Jan 29 at 4:13
show 1 more comment

Actually, your aliases are available after the function is loaded! You can use them in your interactive shell, or in your .bashrc after executing the function.

The restriction is that aliases in a function definition are expanded when the function definition is read, not when the function is evaluated. This is a limitation of bash. So this will work:

function setupLotsOfThings() {
    source aliases.sh
}
setupLotsOfThings
fooAlias

But not this:

function setupLotsOfThings() {
    source aliases.sh
}
function useTheAliases() {
    fooAlias
}
setupLotsOfThings
useTheAliases

If you need aliases that are usable inside functions and can be defined after the function is parsed, make them functions instead. Remember that you can use the command builtin to invoke an external command from a function of the same name.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.