4

Say I have command A and command B but I want B to run when I type A and vice versa. I've tried

alias A='B'
alias B='A'

But this doesn't seem to work for some reason.

How do I do this?

EDIT:

Command B has actually been defined as a function in my .bashrc, so

alias A='command B'
alias B='command A'

doesn't work as suggested.

I guess I could update the question to say that A and B could also be functions defined in bash.

texasflood
  • 501
  • 2
  • 5
  • 14
  • 2
    alias A='command B' B='command A'. Or, if you're certain that the only reference you want to avoid is the alias you've just defined, then alias A=\\B B=\\A. – mikeserv Mar 01 '15 at 12:05
  • Option 2 worked, this could have been posted as an answer, thanks! Option 1 didn't work, I think it is because B is actually a function defined in my .bashrc. I don't understand what you mean by "the only reference you want to avoid is the alias" - could you explain that further please? – texasflood Mar 01 '15 at 12:18
  • I guess you did update the question to say that... haha. – mikeserv Mar 01 '15 at 17:09
  • Oh yeah so I did :P – texasflood Mar 01 '15 at 17:55

1 Answers1

5

So you can get explicit about the way the shell goes about locating commands in a few different ways. You can use...

command command_name

...to instruct the shell only to invoke the command_name if it is a $PATHd executable. This is important in cases like yours because, when you've finished alias A refers to alias B which refers to alias A usually something like 20 times before the alias expansion quits recursing - and which lands you right back where you started. If you wanted to definitely only invoke a $PATHd executable B when calling A and vice versa you can do...

alias A='command B' B='command A'

...which should avoid even calling functions named A or B. Otherwise - because the shell will not expand a quoted alias as an alias but will still call a function even if its command_name is quoted you can quote A and B in the definitions like...

alias A=\\B B=\\A

Ok, so it doesn't recurse twenty times. I could swear I had read that in some man page, but I guess it is at least not true of either bash or ksh (having tested only either of them so far). It, in fact, recurses only twice:

n=0
A(){ echo not B; n=0; }
B(){ echo not A; n=0; }
alias A='echo "$((n+=1))";B' B='echo "$((n+=1))";A'

If I do the above and do...

A

...in bash it prints:

1
2
not B

I guess this is relevant to this from the bash manual:

  • The first word of the replacement text is tested for aliases, but a word that is identical to an alias being expanded is not expanded a second time. This means that one may alias ls to ls -F, for instance, and bash does not try to recursively expand the replacement text.

I had read that before, but I didn't think it would test all expansions leading up to the current expansion when doing so - I figured it would only apply to the latest expansion in the chain. Anyway, that appears to be why.

I did note some interesting behavior otherwise though. After I had already declared both the aliases and the functions I went to rerun the commands, but with a little less intervening white space - like this:

A(){ echo not B; }; B(){ echo not A; }; A; B

...and that did...

1
2
3
4
5
6
not B
7
8
not A

Which means that the shell substituted the alias contents in for the function name - which is not a position one might normally expect an expansion to occur - did the echos and still redefined then executed the functions.

mikeserv
  • 58,310
  • Nice analysis, turns out the expansion rules are quite complicated. I guess you have to careful with circular aliases! Maybe one day I'll look at the code for alias expansion and post a more general explanation. – texasflood Mar 01 '15 at 22:35
  • Ok the solution has worked fine so far, I have been using alias man=\\vman vman=\\man where man is just the ordinary man pages and vman is a custom defined function in my .bashrc. When I run source ~/.bashrc, I get the following error: bash: '\man': not a valid identifier. This error doesn't appear when opening a new terminal. Why does this happen? – texasflood Mar 12 '15 at 17:55
  • @texasflood - I would guess you are quoting the assignment in that case? Like alias 'vman=\\man' maybe? If not I can't say for sure, but you can get a better idea by doing set -vx at the top of your environment file. – mikeserv Mar 12 '15 at 18:01
  • Ok, the output from that produces bash: '\man': not a valid identifier right after the definition of vman(), and then I have the alias, alias man=\\vman vman=\\man, and then it seems to add the alias as per the line below: ++ alias 'man=\vman' 'vman=\man' – texasflood Mar 12 '15 at 18:42
  • And no, I'm not quoting the assignment. You can see the output here: https://gist.github.com/texasflood/988f70634bc749e38f77 – texasflood Mar 12 '15 at 18:42
  • 1
    @texasflood - ahh! I get it. You're sourcing your environment file - so those aliases are already defined when you do. It's exactly what I mentioned here in the answer - the alias is substituting even in the the function definition command - so you're doing vman()... but really doing ${contents of alias vman -> man} \man()... – mikeserv Mar 12 '15 at 20:15
  • It's good to know there wasn't actually a problem with the alias itself! – texasflood Mar 12 '15 at 22:34