Sign up ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

How do I get the ASCII value of the alphabet?

For example, 97 for a?

share|improve this question

7 Answers 7

up vote 36 down vote accepted

Define these two functions (usually available in other languages):

chr() {
  [ "$1" -lt 256 ] || return 1
  printf "\\$(printf '%03o' "$1")"

ord() {
  LC_CTYPE=C printf '%d' "'$1"


chr 65

ord A
share|improve this answer
@dmsk80: +1. For others like me who think they spot a typo: "'A" is correct whereas if you use "A" it will say : A: invalid number . It seems it's done on printf side (ie, in the shell, "'A" is indeed 2 chars, a ' and a A. Those are passed to printf. And in the printf context, it is converted to the ascii value of A, (and is finally printed as a decimal thanks to the '%d'. Use 'Ox%x' to show it in hexa or '0%o' to have it in octal)) – Olivier Dulac Sep 26 '13 at 11:05
-1 for not explaining how it works... joking :D, but seriously what do these printf "\\$(printf '%03o' "$1")", '%03o', LC_CTYPE=C and the single quote in "'$1" do? – razzak Dec 4 '14 at 19:42
Read all the detail in FAQ 71. An excellent detailed analysis. – BinaryZebra Nov 3 at 9:22

You can see the entire set with:

$ man ascii

You'll get tables in octal, hex, and decimal.

share|improve this answer
There's also an ascii package for debian-based distros, but (at least now) the question is tagged as bash, so these wouldn't help the OP. In fact, it's installed on my system and all I get from man ascii is its man page. – Joe Sep 27 '13 at 21:47

If you want to extend it to UTF-8 characters:

$ perl -CA -le 'print ord shift' 😈

$ perl -CS -le 'print chr shift' 128520

With bash, ksh or zsh builtins:

$ printf "\U$(printf %08x 128520)\n"
share|improve this answer
Did you intend to put a square box character or else then the original char is not being displayed in the post and is being replaced by a square box character. – mtk Oct 2 '13 at 7:11
@mtk, You need a browser that displays UTF-8 and a font that has that 128520 character. – Stéphane Chazelas Oct 2 '13 at 7:13
I am on Latest Chrome, and don't think that it doesn't support UTF-8. Would like to know what browser you are on? – mtk Oct 2 '13 at 8:53
@mtk, iceweasel on Debian sid. The font as confirmed by iceweasel's web console is "DejaVu Sans" and I've got ttf-dejavu ttf-dejavu-core ttf-dejavu-extra packages installed which come from Debian with upstream at – Stéphane Chazelas Oct 2 '13 at 9:10
what is the base of 128520? my own ctbl() seems to properly enable me to display it, and to slice the char from the head of a string with printf, but it puts 4*((o1=360)>=(d1=240)|(o2=237)>=(d2=159)|(o3=230)>=(d3=152)|(o4=210)>=(d4=136))‌​ in $OPTARG for the byte values. – mikeserv Nov 18 at 5:52

This works well,

echo "A" | tr -d "\n" | od -An -t uC

echo "A"                              ### Emit a character.
         | tr -d "\n"                 ### Remove the "newline" character.
                      | od -An -t uC  ### Use od (octal dump) to print:
                                      ### -An  means Address none
                                      ### -t  select a type
                                      ###  u  type is unsigned decimal.
                                      ###  C  of size (one) char.

exactly equivalent to:

echo -n "A" | od -An -tuC        ### Not all shells honor the '-n'.
share|improve this answer
Can you maybe add a small explanation? – Bernhard Sep 26 '13 at 12:04
tr to remove "\n" (new line ) from the input. od is used to -t dC is to print in decimal character. – Saravanan Sep 26 '13 at 12:42
echo -n suppresses trailing newline eliminating the need for tr -d "\n" – Gowtham Sep 26 '13 at 16:59
@Gowtham, only with some implementations of echo, not in Unix compliant echos for instance. printf %s A would be the portable one. – Stéphane Chazelas Sep 27 '13 at 8:44

I'm going for the simple (and elegant?) Bash solution:

for i in {a..z}; do echo $(printf "%s %d" "$i" "'$i"); done

For in a script you can use the following:

AscValue=`printf "%d" "'$CharValue"

Notice the single quote before the CharValue. It is obligated...

share|improve this answer
How is your answer different from dsmsk80's answer? – Bernhard Sep 26 '13 at 14:35
My interpretation of the question is "how to get the ASCII values for the values of the alphabet". Not how to define a function to retrieve the ASCII value for one character. So my first answer is a short one-line command to get the ASCII values for the alphabet. – phulstaert Sep 27 '13 at 7:26
I get your point, but I still think the bottom line of both answer is printf "%d". – Bernhard Sep 27 '13 at 9:11
I agree this is a crucial part of the process to get to the result, yet i didn't wanted to make the assumption that xmpirate knew about the "for i in" and the use of a range. If he wanted a list, this could be a real time-saver ;-). Also, future readers might find my additions helpful. – phulstaert Sep 27 '13 at 9:44

Not a shell script, but works

awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }'  

Sample output

xieerqi:$ awk 'BEGIN{for( i=97; i<=122;i++) printf "%c %d\n",i,i }' | head -n 5                                    
a 97
b 98
c 99
d 100
e 101
share|improve this answer
ctbl()  for O                   in      0 1 2 3
        do  for o               in      0 1 2 3 4 5 6 7
                do for  _o      in      7 6 5 4 3 2 1 0
                        do      case    $((_o=(_o+=O*100+o*10)?_o:200)) in
                                (*00|*77) set   "${1:+ \"}\\$_o${1:-\"}";;
                                (140|42)  set   '\\'"\\$_o$1"           ;;
                                (*)       set   "\\$_o$1"               ;esac
                        done;   printf   "$1";   shift
eval '
        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"
        for     c in    ${a+"a=$a"} ${b+"b=$b"} ${c+"c=$c"}\
        do      while   case  $c in     (*\'\''*) ;; (*) ! \
                                 set "" "${c%%=*}='\''${c#*=}$1'\'' $2" "$3"
                        esac;do  set    "'"'\''\${c##*\'}"'$@";  c=${c%\'\''*}
        done;   done;   LC_ALL=C a=$3 c=;set "" "$2 OPTARG='\''${#a}*("
        while   [ 0 -ne "${#a}" ]
        do      case $a in      ([[:print:][:cntrl:]]*)
                        case    $a in   (['"$(printf \\1-\\77)"']*)
                                        b=0;;   (*)     b=1
                        esac;;  (['"$(  printf  \\200-\\277)"']*)
                                        b=2;;   (*)     b=3
                esac;    set    '"$(ctbl)"'     "$@"
                eval "   set    \"\${$((b+1))%"'\''"${a%"${a#?}"}"*}" "$6"'\''
                a=${a#?};set    "$((b=b*100+${#1}+${#1}/8*2)))" \
        done;   eval "   unset   LC_ALL  a b c;${2%?})'\''"
        return  "$((${OPTARG%%\**}-1))"

The first ctbl() - at the top there - only ever runs the one time. It generates the following output (which has been filtered through sed -n l for printability's sake):

ctbl | sed -n l

\035\036\037 !\\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRS\
TUVWXYZ[\\]^_\\`abcdefghijklmnopqrstuvwxyz{|}~\177" "\200\201\202\203\
\267\270\271\272\273\274\275\276\277" "\300\301\302\303\304\305\306\

...which are all 8-bit bytes (less NUL), divided into four shell-quoted strings split evenly at 64-byte boundaries. The strings might be represented with octal ranges like \200\1-\77,\100-\177,\200-\277,\300-\377, where byte 128 is used as a place-holder for NUL.

The first ctbl()'s entire purpose for existence is to generate those strings so that eval may define the second ctbl() function with them literally embedded thereafter. In that way they can be referenced in the function without needing to generate them again each time they are needed. When eval does define the second ctbl() function the first will cease to be.

The top half of the second ctbl() function is mostly ancillary here - it is designed to portably and safely serialize any current shell state it might affect when it is called. The top loop will quote any quotes in the values of any variables it might want to use, and then stack all of the results in its positional parameters.

The first two lines, though, first immediately return 0 and set $OPTARG to same if the function's first argument does not contain at least one character. And if it does, the second line immediately truncates its first argument to only its first character - because the function only handles a character at a time. Importantly, it does this in the current locale context, which means that if a character might comprise more than a single byte, then, provided the shell properly supports multi-byte chars, it will not discard any bytes except those which are not in the first character of its first argument.

        ${1:+":"}       return "$((OPTARG=0))"
        set     "" ""   "${1%"${1#?}"}"

It then does the save loop if at all necessary, and afterward it redefines the current locale context to the C locale for every category by assigning to the LC_ALL variable. From this point on, a character can only consist of a single byte, and so if there were multiple bytes in the first character of its first argument, these should now be each addressable as individual characters in their own right.


It is for this reason that the second half of the function is a while loop, as opposed to a singly run sequence. In most cases it will probably execute only once per call, but, if the shell in which ctbl() is defined properly handles multi-byte characters, it might loop.

        while   [ 0 -ne "${#a}" ]
        do      case $a in      ([[:print:][:cntrl:]]*)
                        case    $a in   (['"$(printf \\1-\\77)"']*)
                                        b=0;;   (*)     b=1
                        esac;;  (['"$(  printf  \\200-\\277)"']*)
                                        b=2;;   (*)     b=3
                esac;    set    '"$(ctbl)"'     "$@"

Note that the above $(ctbl) command substitution is only ever evaluated once - by eval when the function is initially defined - and that forever after that token is replaced with the literal output of that command substitution as saved into the the shell's memory. The same is true of the two case pattern command substitutions. This function does not ever call a subshell or any other command. It will also never attempt to read or write input/output (except in the case of some shell diagnostic message - which probably indicates a bug).

Also note that the test for loop continuity is not simply [ -n "$a" ], because, as I found to my frustration, for some reason a bash shell does:

char=$(printf \\1)
[ -n "$char" ] || echo but it\'s not null\!

but it's not null!

...and so I explicitly compare $a's len to 0 for each iteration, which, also inexplicably, behaves differently (read: correctly).

The case checks the first byte for inclusion in any of our four strings and stores a reference to the byte's set in $b. Afterward the shell's first four positional parameters are set to the strings embedded by eval and written by ctbl()'s predecessor.

Next, whatever remains of the first argument is again temporarily truncated to its first character - which should now be assured to be a single byte. This first byte is used as a reference to strip from the tail of the string which it matched and the reference in $b is eval'd to represent a positional parameter so everything from the reference byte to the last byte in string can be substituted away. The other three strings are dropped from the positional parameters entirely.

               eval "   set    \"\${$((b+1))%"'\''"${a%"${a#?}"}"*}" "$6"'\''
               a=${a#?};set    "$((b=b*100+${#1}+${#1}/8*2)))" \

At this point the byte's value (modulo 64) can be referenced as the string's len:

str=$(printf '\200\1\2\3\4\5\6\7')
ref=$(printf \\4)
echo "${#str}"


A little math is then done to reconcile the modulus based on the value in $b, the first byte in $a is permanently stripped away, and output for the current cycle is appended to a stack pending completion before the loop recycles to check if $a is actually empty.

    eval "   unset   LC_ALL  a b c;${2%?})'\''"
    return  "$((${OPTARG%%\**}-1))"

When $a definitely is empty, all names and state - with the exception of $OPTARG - that the function affected throughout the course of its execution are restored to their previous state - whether set and not null, set and null, or unset - and the output is saved to $OPTARG as the function returns. The actual return value is one less than the total number of bytes in the first character of its first argument - so any single byte character returns zero and any multi-byte char will return more than zero - and its output format is a little strange.

The value ctbl() saves to $OPTARG is a valid shell arithmetic expression that, if evaluated, will concurrently set variable names of the forms $o1, $d1, $o2, $d2 to decimal and octal values of all respective bytes in the first character of its first argument, but ultimately evaluate to the total number of bytes in its first argument. I had a specific kind of workflow in mind when writing this, and I think maybe a demonstration is in order.

I often find a reason to take a string apart with getopts like:

str=some\ string OPTIND=1
while   getopts : na  -"$str"
do      printf %s\\n "$OPTARG"



I probably do a little more than just print it a char per line, but anything's possible. In any case, I haven't yet found a getopts that will properly do (strike that - dash's getopts does it char by char, but bash definitely doesn't):

str=ŐőŒœŔŕŖŗŘřŚśŜŝŞş  OPTIND=1
while   getopts : na  -"$str"
do      printf %s\\n "$OPTARG"
done|   od -tc

0000000 305  \n 220  \n 305  \n 221  \n 305  \n 222  \n 305  \n 223  \n
0000020 305  \n 224  \n 305  \n 225  \n 305  \n 226  \n 305  \n 227  \n
0000040 305  \n 230  \n 305  \n 231  \n 305  \n 232  \n 305  \n 233  \n
0000060 305  \n 234  \n 305  \n 235  \n 305  \n 236  \n 305  \n 237  \n

Ok. So I tried...

while   [ 0 -ne "${#str}" ]
do      printf %c\\n "$str"    #identical results for %.1s
done|   od -tc

0000000 305  \n 220  \n 305  \n 221  \n 305  \n 222  \n 305  \n 223  \n
0000020 305  \n 224  \n 305  \n 225  \n 305  \n 226  \n 305  \n 227  \n
0000040 305  \n 230  \n 305  \n 231  \n 305  \n 232  \n 305  \n 233  \n
0000060 305  \n 234  \n 305  \n 235  \n 305  \n 236  \n 305  \n 237  \n

0000000 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n 305  \n

That kind of workflow - the byte for byte/char for char kind - is one I often get into when doing tty stuff. At the leading edge of input you need to know char values as soon as you read them, and you need their sizes (especially when counting columns), and you need characters to be whole characters.

And so now I have ctbl():

while [ 0 -ne "${#str}" ]
do    ctbl "$str"
      printf "%.$(($OPTARG))s\t::\t$OPTARG\t::\t$?\t::\t\\$o1\\$o2\n" "$str"

Ő   ::  2*((o1=305)>=(d1=197)|(o2=220)>=(d2=144))   ::  1   ::  Ő
ő   ::  2*((o1=305)>=(d1=197)|(o2=221)>=(d2=145))   ::  1   ::  ő
Œ   ::  2*((o1=305)>=(d1=197)|(o2=222)>=(d2=146))   ::  1   ::  Œ
œ   ::  2*((o1=305)>=(d1=197)|(o2=223)>=(d2=147))   ::  1   ::  œ
Ŕ   ::  2*((o1=305)>=(d1=197)|(o2=224)>=(d2=148))   ::  1   ::  Ŕ
ŕ   ::  2*((o1=305)>=(d1=197)|(o2=225)>=(d2=149))   ::  1   ::  ŕ
Ŗ   ::  2*((o1=305)>=(d1=197)|(o2=226)>=(d2=150))   ::  1   ::  Ŗ
ŗ   ::  2*((o1=305)>=(d1=197)|(o2=227)>=(d2=151))   ::  1   ::  ŗ
Ř   ::  2*((o1=305)>=(d1=197)|(o2=230)>=(d2=152))   ::  1   ::  Ř
ř   ::  2*((o1=305)>=(d1=197)|(o2=231)>=(d2=153))   ::  1   ::  ř
Ś   ::  2*((o1=305)>=(d1=197)|(o2=232)>=(d2=154))   ::  1   ::  Ś
ś   ::  2*((o1=305)>=(d1=197)|(o2=233)>=(d2=155))   ::  1   ::  ś
Ŝ   ::  2*((o1=305)>=(d1=197)|(o2=234)>=(d2=156))   ::  1   ::  Ŝ
ŝ   ::  2*((o1=305)>=(d1=197)|(o2=235)>=(d2=157))   ::  1   ::  ŝ
Ş   ::  2*((o1=305)>=(d1=197)|(o2=236)>=(d2=158))   ::  1   ::  Ş
ş   ::  2*((o1=305)>=(d1=197)|(o2=237)>=(d2=159))   ::  1   ::  ş

Note that ctbl() doesn't actually define the $[od][12...] variables - it never has any lasting effect on any state but $OPTARG - but only puts the string in $OPTARG that can be used to define them - which is how I get the second copy of each char above by doing printf "\\$o1\\$o2" because they are set each time I evaluate $(($OPTARG)). But where I do it I'm also declaring a field length modifier to printf's %s string argument format, and because the expression always evaluates to the total number of bytes in a character, I get the whole character on output when I do:

printf %.2s "$str"
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.