Search code examples
windowsbashgitgit-bashgit-for-windows

Cannot use command substitution inside Git Bash on Windows: "bash: command substitution: line 1: syntax error near unexpected token `)'"


In a Bash terminal on Linux, this works fine:

# define some bash func
foo() {
    echo "foo"
}

# Back up your system's default PS1 prompt 1 string, which controls the prompt
# output
if [ -z "$PS1_BAK" ]; then
    PS1_BAK="$PS1"
fi

# Set the PS1 prompt 1 string to run the `foo` function every time the 
# prompt is printed
PS1='$(foo)'"\n$PS1_BAK"

Now, every time I press Enter, it prints foo above my command prompt, as expected. Example:

foo
gabriel@my_computer:~$ 
foo
gabriel@my_computer:~$ 
foo
gabriel@my_computer:~$ 

I want the same behavior on Windows in the Git Bash terminal which comes with Git for Windows. This trick is how I make my terminal automatically tell me the git branch, hash, and tag whenever I am inside a git repo. (See where I set my PS1 string via PS1= here).

However, in Git Bash on Windows, the above code produces this error instead:

bash: command substitution: line 1: syntax error near unexpected token `)'
bash: command substitution: line 1: `foo)'

Why? And more importantly: how do I make it work in Git Bash on Windows?

Note that the Git Bash terminal is based on MSYS2, if that helps.

Update:

Replace the definition of foo above with:

foo() {
    echo "$(pwd)"
}

...to print the present working directory, and you get the same behavior still: Linux works, Git Bash does not. Git Bash has problems with command substitution in the prompt strings.


Solution

  • The already-existing PS1 prompt string uses backtick-style command substitution, so that is what we have to use. Here is the default PS1 variable provided in Git Bash on Windows, as shown by echo "$PS1":

    '\[\033]0;$TITLEPREFIX:$PWD\007\]\n\[\033[32m\]\u@\h \[\033[35m\]$MSYSTEM \[\033[33m\]\w\[\033[36m\]`__git_ps1`\[\033[0m\]\n$'
    

    Notice this part: `__git_ps1`. The __git_ps1 part is a Bash function they provide, so wrapping it in backticks makes it do command substitution.

    So, the solution for use in Git Bash on Windows is to use backtick-style command substitution instead. For my case, it would look like this:

    PS1='`foo`'"\n$PS1_BAK"
    

    Now it works fine in both Linux and in Git Bash on Windows.

    @thatotherguy also pointed out to me in this comment that there's an existing bug report about this in MSYS2.

    See here: MSYS2: bash: '\n' after command substitution in $PS1 causes error #1839.

    Trial and error tells me that only the older style `` instead of $() works in the PS1 prompt string in Git bash.

    I also left a comment about this here: Backticks vs braces in Bash:

    In Git Bash they are not always equivalent. Inside the PS1 prompt string, active command substitution must use backticks!: `command` vs $(command). See my answer here: Cannot use command substitution inside Git Bash on Windows: "bash: command substitution: line 1: syntax error near unexpected token `)'"

    References

    1. Though this answer is entirely written by me, I did have a chat with an AI to prompt me to try the `command` command substitution style instead of the $(command) style.

      I prompted GitHub Copilot with this:

      difference in command substitution with $() vs backticks.

      And it gave me some information which prompted me to try backtick-style command substitution instead.

      I cannot say whether or not `command` is generally more portable, but clearly it works in Git Bash on Windows where $(command) does not.

    See also

    1. PS1 command substitution fails when containing newlines on msys bash
    2. Bug report: MSYS2: bash: '\n' after command substitution in $PS1 causes error #1839