109

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

13 Answers13

86

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}'
Zombo
  • 1
  • 62
  • 391
  • 407
broaden
  • 869
  • 6
  • 2
49

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
  • The final example can even be extended to create a dynamically-generated ASCII table: `seq 98 102 | awk 'BEGIN{print "\n\tDEC\tOCT\tHEX\tCharacter\n\t---\t---\t--\t-"}; {printf("\t%d\t%o\t%x\t%c\n",$1,$1,$1,$1)}; END{printf "\n"}'` _{Thanks also to [this answer](https://unix.stackexchange.com/a/226208/262439) to [a different question](https://unix.stackexchange.com/questions/226206/how-to-add-a-header-and-or-footer-to-a-sed-or-awk-stream)}_ – Jim Grisham Jun 03 '22 at 17:39
20

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

Grisha Levit
  • 8,194
  • 2
  • 38
  • 53
David Hu
  • 3,076
  • 24
  • 26
13

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

printf "\x41\n"
printf "\101\n"
Naaff
  • 9,213
  • 3
  • 38
  • 43
  • +1 for a solution that works even if we don't have the % character on our keyboard :D I was using a Linux terminal from postmarketOS on Nokia N900 and wanted to print it to copy-paste it in commands :) – baptx Feb 21 '22 at 13:43
  • I had to test whether a character was a CarriageReturn (`\r`) in a bash script and I found I had to use the following: `testchar=$'\r'; if [ ${testchar} = $'\r' ]; then echo "is true"; else echo "is false"; fi`. ASCII hex can be used in place of `\r`, ie. `$'\x0D'`. However, the string `"\r"`, or the hex equivalent, didn't work for the conditional, always returning false. – Stephen Hosking Dec 07 '22 at 10:41
  • I can't find \x in the printf man page. Where is this documented? TIA – nortally Apr 07 '23 at 16:27
9

For this kind of conversion, I use perl:

perl -e 'printf "%c\n", 65;'
mouviciel
  • 66,855
  • 13
  • 106
  • 140
  • 4
    or in perl, print chr(65), "\n"; While perl might be a good choice if you're doing a lot of this, using the shell's printf to convert your number into something that can go into a \0 escape sequence works just fine. More complex with unicode, as you need \u support in your printf. – Peter Cordes Nov 28 '13 at 07:34
8

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

shanethehat
  • 15,460
  • 11
  • 57
  • 87
  • This worked, thanks. I wanted to get an Integer (ASCII code) from a typed character. I made a script: {read -n 1 c; echo $( printf "%d" "'${c}" )} – Phaedrus Nov 21 '14 at 18:23
6

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

$ echo -e "\x41" A

Cyber Oliveira
  • 8,178
  • 4
  • 28
  • 18
6

For capital letters:

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

Output:

C
Cyrus
  • 84,225
  • 14
  • 89
  • 153
4

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
chand
  • 41
  • 1
2

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

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

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

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

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

ord 65
Vouze
  • 1,678
  • 17
  • 10
0

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
Roel Van de Paar
  • 2,111
  • 1
  • 24
  • 38
0

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.

Luchostein
  • 2,314
  • 20
  • 24