-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Backtick command substitutions can't nest double quotes #352
Comments
It seems to be the outermost double quotes enclosing the backticks that triggers the issue. Are they even needed?
|
Indeed, ksh93 is the only current shell that gives a syntax error here. However, the original Bourne shell (of which ksh93 is a descendant) throws a syntax error here as well. For information, POSIX specifies:
Bolsky & Korn (1995) give this documentation on page 143:
|
Apart from removing the outer quotes, it seems like the syntax error can be avoided by escaping the parentheses in the regex with backslashes (which will be removed by the backtick comsub): command="`grep -E "^Exec\(\[[^]=]*]\)?=" "$file" | cut -d= -f 2- | first_word`" So that should work on every shell. However, the need to escape parentheses is not documented or specified anywhere AFAICT. edit: That is the fix for the ancient Bourne shell as well. |
If we make
Somehow it's ending up in |
The syntax error is thrown by the first The following debug patch makes a little more sense of what's happening: --- a/src/cmd/ksh93/sh/parse.c
+++ b/src/cmd/ksh93/sh/parse.c
@@ -985,7 +985,10 @@ static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int type)
Namval_t *np;
n = strlen(ap->argval)-1;
if(ap->argval[n]!='=')
+{
+error(ERROR_warn(0),"syntax error at: %c [%s]",ap->argval[n],ap->argval);
sh_syntax(lexp);
+}
if(ap->argval[n-1]=='+')
{
ap->argval[n--]=0; With that, the output is:
So what's happening is that it's actually considering the current argument to end at |
Here's a patch that I think fixes the issue: diff --git a/src/cmd/ksh93/sh/lex.c b/src/cmd/ksh93/sh/lex.c
index c9971851..a6172e4b 100644
--- a/src/cmd/ksh93/sh/lex.c
+++ b/src/cmd/ksh93/sh/lex.c
@@ -313,6 +313,13 @@ int sh_lex(Lex_t* lp)
/* skip over characters in the current state */
state = sh_lexstates[mode];
while((n=STATE(state,c))==0);
+ /*
+ sfprintf(sfstderr,"sh_lex: mode: %d\n",mode); sfsync(sfstderr);
+ sfprintf(sfstderr,"sh_lex: oldmode(lp): %d\n",oldmode(lp)); sfsync(sfstderr);
+ sfprintf(sfstderr,"sh_lex: c: %d\n",c); sfsync(sfstderr);
+ sfprintf(sfstderr,"sh_lex: n: %d\n",n); sfsync(sfstderr);
+ sfprintf(sfstderr,"======\n"); sfsync(sfstderr);
+ */
switch(n)
{
case S_BREAK:
@@ -772,8 +779,17 @@ int sh_lex(Lex_t* lp)
{
if(sh.inlineno > lp->lastline)
lp->lex.last_quote = c;
- mode = oldmode(lp);
- poplevel(lp);
+ /* at this point we know that the previous skipping of characters was done according to the
+ ST_QUOTE state table. we also know that the character that stopped the skipping is also
+ the ending character for the current level. if that character was " and if we are in a
+ `...` statement, then don't switch to the old mode, as we are actually at the innermost
+ section of a "`"..."`" statement, which should be skipped again using the ST_QUOTE state
+ table. */
+ if(c!='"' || !ingrave)
+ {
+ mode = oldmode(lp);
+ poplevel(lp);
+ }
}
else if(c=='"' && n==RBRACE)
mode = ST_QNEST;
diff --git a/src/cmd/ksh93/sh/macro.c b/src/cmd/ksh93/sh/macro.c
index b1ade153..2ced4c9d 100644
--- a/src/cmd/ksh93/sh/macro.c
+++ b/src/cmd/ksh93/sh/macro.c
@@ -2137,6 +2137,10 @@ static void comsubst(Mac_t *mp,register Shnode_t* t, int type)
sp = sfnew(NIL(Sfio_t*),str,c,-1,SF_STRING|SF_READ);
c = sh.inlineno;
sh.inlineno = error_info.line+sh.st.firstline;
+ /*
+ sfprintf(sfstderr,"comsubst: str: %s",str); sfsync(sfstderr);
+ sfprintf(sfstderr,"^^^^^^^^\n"); sfsync(sfstderr);
+ */
t = (Shnode_t*)sh_parse(sp,SH_EOF|SH_NL);
sh.inlineno = c;
type = 1;
If you comment out the fix and uncomment the debug prints, then I hope you'll see what I was going after in this fix: # works
arch/linux.i386-64/bin/ksh -c ': "`: "AB"`"'
# the latter sh_parse() which is called in comsubst() gets wrong input
arch/linux.i386-64/bin/ksh -c ': "`: "A B"`"' |
Thank you very much, that fix makes a lot of sense and I've not discovered any side effects so far. |
The following reproducer causes a spurious syntax error: foo="`: "("`" The nested double quotes are not recognised correctly, causing a syntax error at the '('. Removing the outer double quotes (which are unnecessary) is a workaround, but it's still a bug as every other shell accepts this. This bug has been present since the original Bourne shell. src/cmd/ksh93/sh/lex.c: sh_lex(): case S_QUOTE: - If the current character is '"' and we're in a `...` command substitution (ingrave is true), then do not switch to the old mode but keep using the ST_QUOTE state table. Thanks to @JohnoKing for the report and to @atheik for the fix. Co-authored by: Martijn Dekker <martijn@inlv.org> Resolves: #352
Fixed in ksh 93u+m 2022-05-20 Ref.: ksh93/ksh#352
Fixed in ksh 93u+m 2022-05-20 Ref.: ksh93/ksh#352
I tried setting ksh as
/bin/sh
on my Linux system, and found that it causedxdg-open
to stop working:The line causing the error:
command="`grep -E "^Exec(\[[^]=]*])?=" "$file" | cut -d= -f 2- | first_word`"
Backtick command substitutions in ksh cannot nest double quotes, unlike other shells. This bug is likely related to #199:
The text was updated successfully, but these errors were encountered: