Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I want to write a script that will run

git diff --name-status master..<BRANCH>

but when I run this:

for i in $(git branch | grep -v master); do
   echo $i;
done

I get echo one directory because git branch echo asterisk (I have one directory in current directory)

* <SELECTED BRANCH>

Why * is expanded and how can I prevent that expansion?

UPDATE: I can prevent this by using this:

for i in $(git branch | tr -d '*' | grep -v master);
done;

But why is this happening? Why I need to remove asterisk?

share|improve this question
    
You are missing quotes " " –  val0x00ff Aug 19 at 10:17
    
@val0x00ff where? –  jcubic Aug 19 at 10:19

2 Answers 2

up vote 3 down vote accepted

Unquoted variables and command substitutions like $i or $(git …) apply the split+glob operator to the string result. That is:

  1. Build a string containing the value of the variable or the output of the command (minus final newlines in the latter case).
  2. Split the string into separate fields according to the value of IFS.
  3. Interpret each field as a wildcard pattern and replace it by the list of matching file names; if a pattern doesn't match any file, leave it intact.

The output of git branch | grep -v master (step 1) contains * master which is split (step 2) into two fields * and master; * is replaced (step 3) by the list of file names in the current directory.

You can run set -f to temporarily disable globbing. Another approach is to avoid command substitution and instead parse the input with read. Neither do the right thing though — you'll have spurious branch names, because the git output contains more than what you need (the * flag to indicate the current branch, but also things like remotes/somewhere/foo -> bar for remote-tracking branches). I think the following is safe if inelegant:

for i in $(git branch | sed -e 's/^..//' -e 's/ .*//'); do
   echo $i
done

I think the robust way is to use git-for-each-ref.

for i in $(git for-each-ref --format='%(refname:short)' refs/heads refs/remotes); do
  echo $i
done
share|improve this answer

Text streams like this should be read using a while loop rather than for, which is probably causing the issue (although I cannot reproduce it). A simple way to do this:

git branch | while IFS= read -r line
do
    echo "${line:2}"
done

Comparison:

$ cd -- "$(mktemp -d)"
$ git init
Initialized empty Git repository in /tmp/tmp.MJFmu7q7EH/.git/
$ touch README
$ git commit --allow-empty -m "Initial commit"
[master (root-commit) da46b03] Initial commit
$ git branch foo
$ git branch
  foo
* master
$ for i in $(git branch)
> do
>    echo $i
> done
foo
*
master
$ git branch | while IFS= read -r line
> do
>     echo "${line:2}"
> done
foo
master
share|improve this answer
    
Have the same with while, I'm using GNU bash, version 4.3.11(1)-release (i686-pc-linux-gnu). –  jcubic Aug 20 at 8:34

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.