Bash functions, something strange going on!

bashfunction

I'm new to bash functions but was just starting to write some bits and pieces to speed up my work flow. I like to test this as I go along so I've found myself editing and sourcing my ~/.profile a lot and find ~/. a bit awkward to type…

So the first thing I thought I'd do was the following:

sourceProfile(){
    source ~/.profile
}

editProfile(){
    vim ~/.profile && sourceProfile
}

when running editProfile I'm getting an issue on the sourceProfile call. Initially I was getting the error:

-bash: ~./profile: No such file or directory

Note the lack of typo in my function!

However it works if I use an alias instead.

alias sourceProfile='source ~/.profile'

However after adding that alias and then commenting it out and uncommenting the function I start getting a syntax error instead:

-bash: /home/jonathanramsden/.profile: line 45: syntax error near unexpected token `('
-bash: /home/jonathanramsden/.profile: line 45: `sourceProfile(){'

the proceeding line is:

alias sservice='sudo service'

I'm pretty sure all I did was comment/uncomment! And based on my googling it seems like that's the syntax for defining functions.

Best Answer

Aliases are like some form of macro expansion, similar to the pre-preprocessing done in C with #define except that in shells, there's no clear and obvious delimitation between the pre-processing stage and the interpretation stage (also, aliases are not expanded in all contexts and there can be several rounds of alias expansion like with nested aliases).

When you do:

alias sourceProfile='source ~/.profile'
sourceProfile() {
  something
}

The alias expansion turns it into:

source ~/.profile() {
  something
}

which is a syntax error. And:

alias sourceProfile='source ~/.profile'
editProfile(){
  vim ~/.profile && sourceProfile
}

Turns it into:

editProfile(){
  vim ~/.profile && source ~/.profile
}

So, if you later redefine sourceProfile as a function, editProfile will not call it, because the definition of the editProfile has the expanded value of original alias.

Also, for functions (or any compound command), aliases are only expanded at function definition time (while they're read and parsed), not at run time. So this:

editProfile(){
  vim ~/.profile && sourceProfile
}
alias sourceProfile='source ~/.profile'
editProfile

won't work because sourceProfile was not defined at the time the body of the editProfile function was parsed, and there won't be any alias expansion at the time of running the editProfile function.

So, avoid mixing aliases and functions. And be wary of the implications of using aliases as they're not really commands but some form of macro expansion.