3

According to the bash manual, there is no syntactical difference. The bash-parser on the other hand seems to have a different opinion on that when dealing with arithmetic expressions:

$ echo "$BASH_VERSION"
5.2.15(1)-release
$ echo $((""))
0
$ echo $((''))
bash: '': syntax error: operand expected (error token is "''")

Related:

kvantour
  • 25,269
  • 4
  • 47
  • 72
  • Tokenization and evaluation appears to work slightly differently in arithmetic expressions, and this is not specific to empty strings. Compare `echo $(( 1, '0' ))` and `echo $(( 1, "0" ))` (pay special attention to the error message). – Jeroen Mostert Feb 07 '23 at 13:10
  • 1
    $(( espression )) The expression undergoes the same expansions as if it were within double quotes, but double quote characters in expression are not treated specially and are removed. So it should be like "''" and reading [shell-expansion](https://www.gnu.org/software/bash/manual/bash.html#Shell-Expansions) I found that: After these expansions are performed, quote characters present in the original word are removed unless they have been quoted themselves (quote removal). This could explain why single quote is evaluated. – Lety Feb 07 '23 at 13:19
  • 1
    `(error token is "'0' ")` it's funny that it takes the spaces after `'` as part of token. I wonder what POSIX says. – KamilCuk Feb 07 '23 at 13:19

3 Answers3

4

There seems to be a subtle difference introduced in Bash 5.2. The manual states:

(( expression ))

The arithmetic expression is evaluated according to the rules described below (see Shell Arithmetic). The expression undergoes the same expansions as if it were within double quotes, but double quote characters in expression are not treated specially and are removed. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1.

Source: Bash Reference Manual: Section Conditional Constructs

This implies that (("")) is equivalent to (()) but (('')) is a syntactical error as single quotes are not removed from expression.

kvantour
  • 25,269
  • 4
  • 47
  • 72
  • 1
    That's `((` arithmetic expression like `if (( stuff ))`. In `$((...))` arithmetic _expansion_ there is "undergoes... quote removal` and `quote removal` should remove single quotes. This makes it more confusing, that arithmetic expression and expansion have different documentation. – KamilCuk Feb 07 '23 at 13:21
  • @KamilCuk but is the `(( ))` in the if-statement not the same as the one in `$(())`? Similar to `if command`? On the CLI you can also do `% (( v=1+1 ))` but if you want to substitute it, you have to do `echo val=$((1+1))` – kvantour Feb 07 '23 at 13:24
  • Not really. They use the same characters, and both involve arithmetic, but `$((...))` is not simply an arithmetic compound command prefixed with a `$`. Just like the double-parentheses in `for ((...)); do` are part of the syntax of the `for` loop, rather than the `for` statement accepting an arbitrary compound command for the condition. – chepner Feb 07 '23 at 14:07
  • 1
    But, the same wording appears in the documentation for arithmetic expansion. – chepner Feb 07 '23 at 14:10
  • I have difficulties parsing this section: `...double quote characters in expression are not treated specially are removed`. Does it mean `...double quote characters in expression that are not treated specially are removed` or `...double quote characters in expression are not treated specially and are removed` ? My native language is not English so I'm confused. – M. Nejat Aydin Feb 07 '23 at 16:02
  • @M.NejatAydin the latter – kvantour Feb 07 '23 at 16:02
  • @kvantour Then it would practically mean all double quote characters would be removed from the expression. This is not the case in my experiments: In `$(( $"n" ))`, double quotes are indeed removed and that is equivalent to `$(( $n ))`. But in `$(( "$"n ))`, they are not removed and that results in a syntax error. I think the former interpretation is more plausible. – M. Nejat Aydin Feb 07 '23 at 20:16
  • `$"n"` is a syntax error all around. The intent, I think, is to deny that `$(( "foo" ))` is treated the same as `"\"foo\""`, as you might assume from the explanation that `"foo"` will be treated as if it were in double quotes. – chepner Feb 07 '23 at 21:32
  • @chepner I always understood the `$` as _substitute with value/output of_. Such as `$( ... )` substitute it with the value of subshell `( ... )`, `$(( ... ))` substitute it with the value of `(( ... ))`, `$par` substitute it with the of `par`, ... – kvantour Feb 08 '23 at 08:28
  • @chepner is `$"n"` not ANSI-C quoting? – kvantour Feb 08 '23 at 08:29
  • @chepner `$"n"` is [Locale-Specific Translation](https://www.gnu.org/software/bash/manual/bash.html#Locale-Translation). I'm not sure how it is treated in `$((...))` – M. Nejat Aydin Feb 08 '23 at 10:29
  • Gah, I was even thinking about that. The most I can say is I apparently had a difference between `$"n"` and `$"n"` in my head when I typed that. (Yes, it's the same expression. That's how ridiculous my comment is.) – chepner Feb 08 '23 at 13:44
  • 1
    @kvantour `$'n'` is ANSI quoting. `$"n"` is, of course, related to translation. – chepner Feb 08 '23 at 13:44
  • The preceding-`$` is a mnemonic, based on parameter expansion, not an arbitrary operator that turns any statement into an analogous expression. There's no such thing as `$[[...]]`, for example. (Ignore the ridiculously obsolete `$[...]` syntax for arithmetic expression.) – chepner Feb 08 '23 at 13:45
3

Exploring how different shell brands handles this

  • bash version 5.1-6
  • dash version 0.5.11
  • ksh93 version 1.0.0~beta.2
  • zsh version 5.8.1

Ksh93 seems to show the most distinctive behavior.

What it teaches is:

Within an arithmetic context, shells interpret a single quote as the single quote character itself, but not as the quoted literal value.

#!/usr/bin/env sh

for shell in bash dash ksh93 zsh; do
  printf 'Testing with %s:\n' "$shell"
  "$shell" <<'EOF'
LC_ALL=C
echo "$((''))"
EOF
  echo
done

Output:

Testing with bash:
bash: line 2: '': syntax error: operand expected (error token is "''")

Testing with dash:
ash: 2: arithmetic expression: expecting primary: "''"

Testing with ksh93:
39

Testing with zsh:
zsh: bad math expression: illegal character: '
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • Is this the same or related to single-quote-prefixed arguments to `printf`? I can't seem to find that documented in the `ksh` man page, but it does exist: `printf '%d\n' "''"` outputs 39. – chepner Feb 07 '23 at 14:26
  • Ok, I see `ksh` treats that argument as an arithmetic context, induced by `%d` I guess. I'm not sure *that's* documented; the only note on arguments refers to the ANSI-C formatting rules, which I didn't think included doing implicit arithmetic of string arguments. – chepner Feb 07 '23 at 14:28
  • @chepner I found in the [oracle printf documentation](https://docs.oracle.com/cd/E36784_01/html/E36870/printf-1.html#scrolltoc) that for the argument the following extension is applicable: _If the leading character is a single- or double-quote, the value is the numeric value in the underlying codeset of the character following the single- or double-quote._ – kvantour Feb 07 '23 at 21:26
  • Yes, this is POSIX-standard behavior for `printf`; it's odd that `ksh` doesn't document it in the man page for its built-in version. – chepner Feb 07 '23 at 21:30
2

Seems like a bug, as the manual says

All tokens in the expression undergo parameter and variable expansion, command substitution, and quote removal.

choroba
  • 231,213
  • 25
  • 204
  • 289
  • 1
    The sentence previous to this quote implies that double quotes are removed first, though. I'm leaning towards this being a bug, but in the design, not the implementation of the documented behavior. Personally, I'd like to see an example where single quotes do anything but produce a syntax error when retained. – chepner Feb 07 '23 at 14:15
  • 1
    @chepner ksh93 interpret single-quote within an arithmetic context as the character code number of the single-quote `39`. – Léa Gris Feb 07 '23 at 14:24
  • 1
    @LéaGris That seems to be a whole other ball of undocumented weirdness :) – chepner Feb 07 '23 at 14:31
  • 1
    The inconsistency with `(( '' ))` (no dollar, no error) seems to indicate a bug, too. – choroba Feb 07 '23 at 14:50