2

[Note - I solved the problem I ran into using a bash function, but I want to understand why my initial attempt didn't work.]

I run git in Windows, and use the Git Bash command line application to manage repositories. I have several repositories that I often want to pull all at once. Previously I was doing this just by entering the following on the command line:

for i in *; do cd $i; git pull --rebase; cd ..; done;

To save time, I decided to create an alias for this. So, I created a .bashrc file in the home directory (C:/git in my case) and added the line

alias pr="for i in *; do cd $i; git pull --rebase; cd ..; done;"

However, this didn't work at all, the output was

sh.exe" cd: /etc/profile.d/*.sh: No such file or directory

followed by git complaining that it wasn't in a repository. The output would be repeated over 20 times for a single call. The root of the MinGW filesystem, where the /etc above derives from, is where I installed git to (C:/Program Files (x86)/Git).

Now, I solved this problem in the end by creating a function in my .bashrc file instead, like so:

pr(){
    for i in *
    do
        cd $i
        git pull --rebase
        cd ..
    done
}

So, my problem is solved, but I do want to understand why my initial approach didn't work. There's clearly something about aliases I don't understand that is, presumably, mangling the 'i in *' bit. My expectation was that bash would substitute the string the alias maps to and then evaluate it, but it doesn't seem to be that straightforward.

Tarrenam
  • 382
  • 2
  • 11
  • 2
    What happens if you add `echo "i=$i"` right before the `pr` alias assignment and re-source? – Reinstate Monica Please Nov 05 '14 at 11:26
  • 2
    Also, you probably want to make it short-circuit conditional `cd "$i" && git pull --rebase && cd ..`. Otherwise you could get some weird results (`$i` is not a directory, but you still run `git` and move up a level, etc...) – Reinstate Monica Please Nov 05 '14 at 11:34
  • possible duplicate of [bash alias command with both single and double quotes](http://stackoverflow.com/questions/20111063/bash-alias-command-with-both-single-and-double-quotes) – tripleee Nov 05 '14 at 13:36
  • @BroSlow - just like the answer below, you put me on the right path with your `echo` suggestion. Thanks for the `&&` suggestion, too. – Tarrenam Nov 05 '14 at 14:05

1 Answers1

6

I haven't analyzed this in full, but it has to do with the time at which your $i in the alias gets expanded. The way you're doing this, $i will probably be initialized during alias creation. In the function version, it will be interpreted at run-time. Try defining the alias as you've shown, then run:

alias pr  # show how the alias is defined

Bash will happily postpone expansion of $-variables if you define the alias with single quotes. The following should work as well as your function.

alias pr='for i in *; do cd $i; git pull --rebase; cd ..; done;'
Karel Kubat
  • 1,635
  • 11
  • 12
  • 1
    Someone, please explain the `-1` – Reinstate Monica Please Nov 05 '14 at 11:36
  • +1 This is absolutely correct; the `$i` inside the double quotes was expanded before the alias was defined. Parameter expansion is not performed inside single quotes or (as the OP discovered) in the body of a function when the function is defined. – chepner Nov 05 '14 at 13:29
  • Aha, it seems you're right. `alias pr` gives the following output: `alias pr='for i in *; do cd /etc/profile.d/*.sh; git pull --rebase; cd ..; done;'` So it's a problem with `$i` being expanded and not, as I thought, something to do with `*`. I've verified that it does work with single quotes, too. – Tarrenam Nov 05 '14 at 14:00