19

This command succeeds

$ PS1='$(date +%s) $ '
1391380852 $

However if I add a newline it fails

$ PS1='$(date +%s)\n$ '
bash: command substitution: line 1: syntax error near unexpected token `)'
bash: command substitution: line 1: `date +%s)'

If I use backticks it works

$ PS1='`date +%s`\n$ '
1391381008
$

but backticks are discouraged. So what is causing this error?

GNU bash, version 4.2.45(6)-release
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Zombo
  • 1
  • 62
  • 391
  • 407
  • 3
    It works fine for me. What bash version are you using? I have `GNU bash, version 4.2.45(2)-release` installed. – Rubens Feb 02 '14 at 22:50
  • 2
    It's work for me too even with bash 3 and KSH (besides the `\n` with KSH). – Idriss Neumann Feb 02 '14 at 23:23
  • Any particular vendor package of 4.2.45(6), or is this stock upstream? – Charles Duffy Feb 04 '14 at 22:22
  • It's hard to imagine which a character which **follows** the closing parenthesis causes that parenthesis to be considered a literal part of the command being substituted, rather than a closing token for command substitution. The parsing must be convoluted, making backward jumps in the stream, or bizarre lookaheads. – Kaz Aug 19 '15 at 21:44
  • @Kaz, `))` is a different token from `)`, so some amount of lookahead is mandatory. But yes, this is a funny one -- that it's platform-dependent makes it doubly so. – Charles Duffy May 26 '16 at 17:13
  • This is a weird & esoteric problem. I just hit something similar all these years later, in Git bash on Windows.... I just resorted to using backticks. If it works... – spechter Oct 04 '21 at 11:43

3 Answers3

26

You can disambiguate the parsing easily, to prevent hitting any such bug (though I can't reproduce it myself):

PS1='$(date +%s)'$'\n$ '

This $'\n' syntax parses to a literal newline character, whereas '\n' parses to a string containing a two-character \n escape sequence.

For more info on how $'' differs from '' (expanding backslash-escaped sequences) refer to the Bash Hackers Wiki.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 2
    Works like a charm. The problem arose in my case using MSYS2 compiled bash `(GNU bash, version 4.2.45(6)-release (x86_64-pc-msys)`). Thank you. – Matt Feb 06 '14 at 04:18
  • Using newest version of Git for Windows with Git Bash - needed to edit `%USERDIR%/.profile` and change lines 8 and 18 using this answer. E.g. line 8 was `PS1='\[\e]0;${PWD##*/}\a\]\n'` and needed changed to `PS1='\[\e]0;${PWD##*/}\a\]'$'\n'` and now I don't get the substitution errors. Thank you, sir. – jdforsythe Mar 28 '16 at 11:03
  • @JustGoscha, could you post a log, copy-and-pasted, showing you running this code and the resulting behavior in a gist? (That is, actually doing the assignment to PS1 on the command line, just to be sure we aren't seeing any interaction from other dotfile contents). – Charles Duffy Apr 11 '16 at 15:20
  • Okay, actually this code works. But I have the double quotation marks in my example and my code goes over several lines. And I don't know how to escape the \n in this case. Here's my code: http://pastebin.com/Jaym3ARk – justGoscha Apr 11 '16 at 15:24
  • 2
    Okay I solved it for double quotes `"bla \n bla"` becomes `"bla "$'\n'" bla"`. Solved by good old trial and error, and reading about Bash escaping... – justGoscha Apr 11 '16 at 15:33
  • @JustGoscha, ...btw, pastebin.com is awful for anyone without adblock; I suggested gist for a reason. – Charles Duffy Apr 11 '16 at 15:44
  • This is so weird. The same prompt works on Mac, Linux and Cygwin, but not in MSYS. Splitting the string with quotes, and inserting the $ char, fixes it, but I have no idea why. The explanation given by Duffy is not really helping as it does not reflect the code - where is the `$''` bit in the actual code? – oligofren Sep 16 '16 at 21:04
  • @oligofren, do you see `$'\n$ '` in my answer? Look a bit closer if not. `$''` is part of the *workaround*, not the bug. – Charles Duffy Sep 16 '16 at 21:07
  • 1
    @oligofren, ...one thing to keep in mind is that shell doesn't require a word to have the same quoting style the same way through; you can do, for instance, `literal"double-quoted"'single-quoted'` to generate a single word, different parts of which are treated with three different quoting types at parse time. That's exactly what I'm doing in the answer, switching to the `$''` quoting type to inject a literal newline. – Charles Duffy Sep 16 '16 at 21:09
  • I understood both points, but not how the extra $ changes anything. I am not sure how the $ gets parsed to be frank. It surely does not get parsed to a literal $ sign. And not how it can affect the following newline either. I see it does, of course... – oligofren Sep 16 '16 at 21:16
  • 1
    @oligofren, it's a different quoting form that causes things to be parsed differently. `$'\n'` parses to a one-character literal newline, whereas `'\n'` parses to a two-character sequence, a literal backslash followed by a literal `n`. – Charles Duffy Sep 16 '16 at 21:20
  • Ah, now that makes sense! It modifies parsing by doing (some/full?) evaluation of the string. That should be part of the answer. – oligofren Sep 16 '16 at 21:23
  • @oligofren, "evaluation of the string"? I find that phrase misleading, since it performs only a very specific kinds of expansion, not general-purpose evaluation. There are no `$()` expansions, no `$foo` expansions... really, nothing but your backslash-escape-sequence replacement with literals. And the answer **already does indicate** that it does that; to quote the last paragraph: "This `$''` syntax places a literal newline inside the variable's contents, rather than a `\n` escape sequence." -- I don't know how I could make it more clear. – Charles Duffy Sep 16 '16 at 21:24
  • @oligofren, ..."evaluation" typically means something more like what `eval` performs, which is absolutely not the case here. `$''` is a literal syntax (that is, a syntax used to express literals), not an expansion type. – Charles Duffy Sep 16 '16 at 21:27
  • No, that last sentence is what got me confused. If it had said $ will do backslash expansion on the following single quoted string that would be perfectly clear. – oligofren Sep 16 '16 at 21:27
  • Yeah, sorry about wrong term usage. What you said :) – oligofren Sep 16 '16 at 21:28
  • Just to be clear, if instead of "This $'' syntax..." had said "This $'\n ' syntax..." I would not have gotten confused :-) – oligofren Sep 16 '16 at 21:32
  • 1
    Thank you -- hopefully the edit now applied meets with your approval. – Charles Duffy Sep 16 '16 at 21:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/123580/discussion-between-oligofren-and-charles-duffy). – oligofren Sep 17 '16 at 06:17
3

I had a similar issue with .git-prompt when I tried to include it in my PS1 on bash (MSYS2) on Windows. The problem is the \n, if I remove it everything run smoothly but I want to break-line.

By the way on Linux everything is working fine.

The bash is run is: 4.3.42(5)-release (x86_64-pc-msys)

Old, problematic PS1:

PS1='\e[32m\]\u@\h \e[36m\]\w \e[32m\]$(__git_ps1 "(%s)")\nλ \e[0m\]$(tput sgr0)'

Fixed:

PS1='\e[32m\]\u@\h \e[36m\]\w \e[32m\]$(__git_ps1 "(%s)")'$'\nλ \e[0m\]'

Simplified version (no colors, copy-paste-edit it):

PS1='\u@\h \w $(__git_ps1 "(%s)")'$'\n$ '

Cheers Charles Duffy finding the problem!

Community
  • 1
  • 1
tvl
  • 3,868
  • 2
  • 16
  • 35
1

The $'\n' hack was still resulting in a syntax error for me in my git-bash Windows VSCode terminal. After a lot of trial and error, I managed to fix it by using the octal equivalent of the newline character, which is \012.

In short, replace \n with \012 wherever it's giving you a syntax error.

Word Demon
  • 73
  • 2
  • 8