Integer ASCII value to character in BASH using printf

BashAsciiPrintf

Bash Problem Overview


Character to value works:

$ printf "%d\n" \'A
65
$ 

I have two questions, the first one is most important:

  • How do I take 65 and turn it into A?
  • 'A converts an ASCII character to its value using printf. Is the syntax specific to printf or is it used anywhere else in BASH? (Such small strings are hard to Google for.)

Bash Solutions


Solution 1 - Bash

One line

printf "\x$(printf %x 65)"

Two lines

set $(printf %x 65)
printf "\x$1"

Here is one if you do not mind using awk

awk 'BEGIN{printf "%c", 65}'

Solution 2 - Bash

This works (with the value in octal):

$ printf '%b' '\101'
A

even for (some: don't go over 7) sequences:

$ printf '%b' '\'{101..107}
ABCDEFG

A general construct that allows (decimal) values in any range is:

$ printf '%b' $(printf '\\%03o' {65..122})
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz

Or you could use the hex values of the characters:

$ printf '%b' $(printf '\\x%x' {65..122})
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz

You also could get the character back with xxd (use hexadecimal values):

$ echo "41" | xxd -p -r
A

That is, one action is the reverse of the other:

$ printf "%x" "'A" | xxd -p -r
A

And also works with several hex values at once:

$ echo "41 42 43 44 45 46 47 48 49 4a" | xxd -p -r
ABCDEFGHIJ

or sequences (printf is used here to get hex values):

$ printf '%x' {65..90} | xxd -r -p 
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Or even use awk:

$ echo 65 | awk '{printf("%c",$1)}'
A

even for sequences:

$ seq 65 90 | awk '{printf("%c",$1)}'
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Solution 3 - Bash

For your second question, it seems the leading-quote syntax (\'A) is specific to printf:

> 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.

From https://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

Solution 4 - Bash

One option is to directly input the character you're interested in using hex or octal notation:

printf "\x41\n"
printf "\101\n"

Solution 5 - Bash

For this kind of conversion, I use perl:

perl -e 'printf "%c\n", 65;'

Solution 6 - Bash

If you want to save the ASCII value of the character: (I did this in BASH and it worked)

{
char="A"

testing=$( printf "%d" "'${char}" )

echo $testing}

output: 65

Solution 7 - Bash

If you convert 65 to hexadecimal it's 0x41:

$ echo -e "\x41" A

Solution 8 - Bash

For capital letters:

i=67
letters=({A..Z})
echo "${letters[$i-65]}"

Output:

C

Solution 9 - Bash

Here's yet another way to convert 65 into A (via octal):

help printf  # in Bash
man bash | less -Ip '^[[:blank:]]*printf'

printf "%d\n" '"A'
printf "%d\n" "'A"

printf '%b\n' "$(printf '\%03o' 65)"

To search in man bash for \' use (though futile in this case):

man bash | less -Ip "\\\'"  # press <n> to go through the matches

Solution 10 - Bash

this prints all the "printable" characters of your basic bash setup:

printf '%b\n' $(printf '\\%03o' {30..127})

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Solution 11 - Bash

Here is a solution without eval nor $() nor `` :

ord () {
 local s
 printf -v s '\\%03o' $1
 printf "$s"
}

ord 65

Solution 12 - Bash

The following script prints aaa...zzz (i.e. aaa > aab > aac > ... > zzx > zzy > zzz)

for i in $(seq 0 25); do FIRST=$(printf \\$(printf '%03o' $[97+$i]) | tr -d '\n'); for f in $(seq 0 25); do SECOND=$(printf \\$(printf '%03o' $[97+$f]) | tr -d '\n'); for g in $(seq 0 25); do THIRD=$(printf \\$(printf '%03o' $[97+$g]) | tr -d '\n'); echo "${FIRST}${SECOND}${THIRD}"; done; done; done

Solution 13 - Bash

Given that there are not that many 1-byte characters, but only 256, they can quickly be precomputed at your script startup:

declare -ag CHARS=()
for REPLY in {{0..9},{a..f}}{{0..9},{a..f}}; do
  printf -v CHARS[${#CHARS[@]}] "\x$REPLY"
done

(I reused the discardable REPLY variable, but you can local your own inside an init function)

Then...

$ echo "${CHARS[65]}"
A
$ i=65
$ echo "${CHARS[i]}"
A

Beware of \x00, whose value is not properly handled in Bash because it is the string terminator character:

$ var=$'qwe\x00rty'
$ echo ${#var}
3
$ echo "<${var}>"
<qwe>

So, "${CHAR[0]}" will always be a problem under Bash.

This way, you avoid output capture, inline text substitution and re-parsing once and again for every time you need to process one character; which is even worse for subprocess IPC through xxd, awk or perl.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser14070View Question on Stackoverflow
Solution 1 - BashbroadenView Answer on Stackoverflow
Solution 2 - Bashuser2350426View Answer on Stackoverflow
Solution 3 - BashDavid HuView Answer on Stackoverflow
Solution 4 - BashNaaffView Answer on Stackoverflow
Solution 5 - BashmouvicielView Answer on Stackoverflow
Solution 6 - BashMagicMercury86View Answer on Stackoverflow
Solution 7 - BashCyber OliveiraView Answer on Stackoverflow
Solution 8 - BashCyrusView Answer on Stackoverflow
Solution 9 - BashchandView Answer on Stackoverflow
Solution 10 - BashAG6HQView Answer on Stackoverflow
Solution 11 - BashVouzeView Answer on Stackoverflow
Solution 12 - BashRoel Van de PaarView Answer on Stackoverflow
Solution 13 - BashLuchosteinView Answer on Stackoverflow