20

I want to obtain the ASCII number of a character, so I have the following:

VAR="a"
NUM=$(printf "%d" "'$VAR")
echo $NUM

What does '$ mean in this context? Can someone point me to a documentation to understand the syntax? I don't understand if its part of $(...) or printf or bash.

2
  • 2
    Try printf "%d" "'a" and see what happens
    – Fox
    Jan 30, 2020 at 16:14
  • 1
    With bash's builtin printf, you can do printf -v NUM "%d" "'$VAR" without having to spawn a subshell. Jan 30, 2020 at 20:12

3 Answers 3

28

'$ doesn't mean anything special. With %d in printf, it tries to evaluate the argument as an integer expression. 'a is taken to be the char a, or the integer 97. You'd get the same result even if you didn't use variable expansion:

$ printf %d\\n "'a'"
97
$ printf %d\\n "'0'"
48
$ printf %d\\n "'"$'\1'
1

From the bash documentation on printf (emphasis mine):

Arguments to non-string format specifiers are treated as C language constants, except that a leading plus or minus sign is allowed, and if the leading character is a single or double quote, the value is the ASCII value of the following character.

Any characters left are ignored, as noted in the comments.

3
  • 6
    Do you want to add anything about the use of the single single quote as opposed to balanced singled quotes? All your examples (except the last) use balanced quotes, but without comment.
    – Kusalananda
    Jan 30, 2020 at 16:39
  • 4
    Relevant text from the bash man page: "Arguments to non-string format specifiers are treated as C constants, except that a leading plus or minus sign is allowed, and if the leading character is a single or double quote, the value is the ASCII value of the following character." Jan 30, 2020 at 20:15
  • 2
    You have written an extra apostrophe after the letters in your first two examples. So the second argument of your first example should be "'a" and not "'a'". Any characters after the first letter are ignored, so you won't get an error if you add that extra apostrophe, but it's not correct.
    – AndyB
    Jan 31, 2020 at 4:07
21

The ' is part of the printf argument (see the Arguments section here).

The $ is part of regular shell expansion, so the printf arguments become %d and 'a.

Finally, $(...) construct is Command Substitution, which in this case means that NUM has its value set to the output of the printf function.

1
  • 7
    Before the single-quote becomes part of the argument printf gets, it's also important to note that, it itself is inside the double-quoted string, so it's just an ordinary character as far as the shell syntax is concerned. (It doesn't start a quoted string as it would if it weren't escaped or quoted itself.)
    – ilkkachu
    Jan 31, 2020 at 8:10
12

As already said, the '$ doesn't mean anything special by itself. Your "'$VAR" gets expanded to 'a, which will be passed as an argument to the printf utility.

Then come the interesting part -- an obscure, but standard feature of printf (the shell utility, not the function from the C language).

According to the SuSv4 standard (emphasis mine):

The argument operands [to printf] shall be treated as strings if the corresponding conversion specifier is b, c, or s, and shall be evaluated as if by the strtod() function if the corresponding conversion specifier is a, A, e, E, f, F, g, or G.

Otherwise [e.g. if the conversion specifier is d], they shall be evaluated as unsuffixed C integer constants, as described by the ISO C standard, with the following extensions:

  • A leading <plus-sign> or <hyphen-minus> shall be allowed.

  • If the leading character is a single-quote or double-quote, the value shall be the numeric value in the underlying codeset of the character following the single-quote or double-quote.

  • Suffixed integer constants may be allowed.

If your shell supports multibyte characters (as with UTF-8, the default on any modern system), that numeric value will be that of the complete character, not of its leading byte:

% printf '%d\n' '"á' "'é"
225
233
% printf 'U+%X\n' '"猫儿'
U+732B

Notice that that's not the "ASCII value" of the character -- the description for the bash manpage is at best misleading.

However, bash (and most other shells but yash) are not standard conformant, because they will perform that translation even with float specifiers like f or g, which is in clear violation of the first paragraph above, which says that strtod() should be used in that case:

% bash --posix -c 'printf "%f\n" \"Q'
81.000000
1
  • 1
    Welcome, great answer:: and username.
    – bishop
    Feb 1, 2020 at 2:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .