1

I found this handy bash function for selecting and checking out a previous branch a while back. It worked like a charm. The key bits are:

  BRANCHES=(
    $(
      git reflog |
        egrep -io "moving from ([^[:space:]]+)" |
        awk '{ print $3 }' |        # extract 3rd column
        awk ' !x[$0]++' |           # Removes duplicates.  See http://stackoverflow.com/questions/11532157
        egrep -v '^[a-f0-9]{40}$' | # remove hash results
        while read line; do         # verify existence
          ([[ $CHECK_EXISTENCE = '0' ]] || git rev-parse --verify "$line" &>/dev/null) && echo "$line"
        done |
        head -n "$NUM"
    )
  )

  if [[ $INTERACTIVE = '1' ]]; then
    PS3="Choose a branch: "

    select d in "${BRANCHES[@]}"; do
      test -n "$d" && break
      echo ">>> Invalid Selection"
    done

    git checkout "$d"
  else
    printf '%s\n' "${BRANCHES[@]}"
  fi

Our branch names are all of the form bug/... or task/....

For some reason, today it started failing. When I debugged it with set -x, I discovered that echo ${BRANCHES[@]}, which select appears to evaluate unquoted, is removing the /.

To simplify/verify the issue, I ran this:

foo=( "a/b" "c/d" )
echo $foo
# prints a b
echo ${foo[@]}
# prints a b c d
echo "${foo[@]}"
# prints a/b c/d

This issue is wreaking havoc all over my aliases/functions, as you might expect.

Strangely, while the function in the script prints:

1) master
task
2) 16658-...
task
3) 16525-...
Choose a branch:

Selecting from foo prints as expected:

select f in "${foo[@]}"; do
  echo $f
  break
done
# prompts:
# 1) a/b
# 2) c/d

so I may be wrong about the root cause, but I doubt it. Selecting 1 in the prompt above echos a b.

In fact...

foo=a/b
echo $foo
# prints a b
echo "$foo"
# prints a/b

foo="a/b"
echo $foo
# prints a/b

If it matters, I'm using git-bash for Windows rather than unix bash, but I can't imagine that's the issue.

Any ideas?

4
  • 2
    Try unset IFS.
    – user313992
    Commented Oct 12, 2020 at 18:44
  • Oh, that fixed it! Thank you! Write it as an answer, and I'll mark it.
    – dx_over_dt
    Commented Oct 12, 2020 at 18:45
  • You shouldn't be using @ in an unquoted expression Commented Oct 12, 2020 at 19:47
  • @roaima I wasn't. select appeared to.
    – dx_over_dt
    Commented Oct 13, 2020 at 2:02

1 Answer 1

2

When you echo something unquoted, it is subjected to what is known as split+glob. This means that what you echo will be split on any characters in the variable IFS and then treated as a glob and, if it matches anything, that glob will be expanded. The part you care about here is the split. What you describe cannot happen with the default value of IFS (which is a space, a tab and a newline). If you are seeing this behavior, then you (or something in your shell session) has set IFS to something else, most likely a /. To illustrate:

$ foo=( "a/b" "c/d" )
$ echo ${foo[@]}
a/b c/d

## Now, change IFS
$ IFS='/'
$ echo ${foo[@]}
a b c d
## Of course, properly quoting fixes it
$ echo "${foo[@]}"
a/b c/d

## And so does un-setting IFS
$ unset IFS
$ echo ${foo[@]}
a/b c/d

This is just one of the reasons why you should always quote your variables. For more on that see Security implications of forgetting to quote a variable in bash/POSIX shells and Why does my shell script choke on whitespace or other special characters?.

Note that this explains why your examples are failing, not why your original script is failing. The original has everything properly quoted as far as I can tell.

1
  • Yeah, I can't explain why the script was failing either, given the quoting.
    – dx_over_dt
    Commented Oct 13, 2020 at 2:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .