4

echo $GOPATH will print:

/mnt/star/program/go/package:/mnt/star/git_repository/workspace/go_workplace

There are 2 directories, I want to append the first directory's sub-directory bin/ to $PATH.

If I write $PATH=$PATH:$GOPATH/bin, then actually it appends 2 directories to $PATH:

  • /mnt/star/program/go/package
    This only contains directories, it should be /mnt/star/program/go/package/bin.
  • /mnt/star/git_repository/workspace/go_workplace/bin
    This actually shouldn't be added to $PATH.

BTW, there are cases that $GOPATH only contains one directory, so that simply appending $GOPATH/bin will work.

I am looking for a solution that fits both cases. So, how do I write this in a bash config file?

6

You can use:

PATH="$PATH:${GOPATH%%:*}/bin"

Or

PATH="$PATH:${GOPATH%:*}/bin"

Both will work because there can be at most one :.

It will remove the part after :. So, in your first case, it will remove the second directory and in your second case, there will be no pattern like :*, so there will be no change in the directory name.

  • Yes, that's exactly it! Only improvement this could get is default to ~/go if not defined (which is default since Go 1.8 I believe) but this answers the asked question perfectly, thanks! – filbranden Feb 13 '19 at 5:27
  • 1
    Works like a charm. – Eric Wang Feb 13 '19 at 6:19
3
PATH="$PATH:${GOPATH%%:*}/bin"

The asterisk is a glob, not a regular expression. Two percentage signs means to remove the maximum that it can match from the back, so even if there were three or more directories, you would only get the first one.

If there's nothing to remove, it doesn't remove anything, so you get the only path if there's only one.

  • It's the opposite... It's supposed to keep the first part, so you need to remove the last part. This is definitely the best answer, so if you fix it to PATH=$PATH:${GOPATH%%:*}/bin I'll definitely upvote it! – filbranden Feb 13 '19 at 5:26
  • I read the question backwards. You're right, @filbranden. I'll fix it. – Ken Jackson Feb 13 '19 at 5:29
1

Here’s an alternative answer using arrays; unfortunately this requires two steps to assign to the answer but I mention it anyway since it’s more general and potentially more versatile than the other answers:

IFS=: read -ra arr_gopath <<< "$GOPATH"
PATH="$PATH:${arr_gopath[0]/%//bin}"

The advantage with this approach is that you can easily specify any component of the GOPATH, not just the first one. Or, to append all elements, followed by /bin, to PATH:

PATH="$PATH:$(IFS=:; echo "${arr_gopath[*]/%//bin}")"

In both cases (splitting, joining) we set the internal field separator (IFS) to the colon character :. The substitution ${var/%//bin} appends /bin to the end of a chosen variable.

That said, if you wanted to append all GOPATH components, suffixed with /bin, to the PATH then the following would be simpler and doesn’t require arrays. It does, however, require that GOPATH is not empty:

PATH="$PATH:${GOPATH//://bin}/bin"

(${var//pattern/subst} replaces all occurrences of pattern with subst, while ${var/pattern/subst}, used above, only replaces the first one.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.