Integer ASCII value to character in BASH using printf

Viewed 160178

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 Answers

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

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

For this kind of conversion, I use perl:

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

For capital letters:

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

Output:

C

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

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

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

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

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

ord 65

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

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.

Related