Differences between revisions 5 and 15 (spanning 10 versions)
Revision 5 as of 2008-11-22 14:09:08
Size: 1733
Editor: localhost
Comment: converted to 1.6 markup
Revision 15 as of 2009-10-16 14:55:19
Size: 2668
Comment: My octal conversion formula was way wrong. Sorry.
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
Line 3: Line 4:
If you have a known octal or hexadecimal value (at script-writing time), you can just use `printf`:
Line 4: Line 6:
If you have a known octal or hexadecimal value (at script-writing time), you can just use `printf`:
Line 12: Line 13:
Line 16: Line 18:
 
Line 20: Line 22:
 
Line 28: Line 30:
   hex() {     hex() {
Line 37: Line 39:
 
Line 41: Line 43:
Line 44: Line 45:
 ''Tricky? Rather, it's using a feature that I can't find documented anywhere -- putting a single quote in front of an integer. Neat effect, but how on '''earth''' did you find out about it? Source diving? -- GreyCat''  . ''Tricky? Rather, it's using a feature that I can't find documented anywhere -- putting a single quote in front of an integer. Neat effect, but how on '''earth''' did you find out about it? Source diving? -- GreyCat''
  . ''It validates The Single Unix Specification: "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." (see [[http://www.opengroup.org/onlinepubs/009695399/utilities/printf.html|printf()]] to know more) -- mjf''
Line 46: Line 48:
  ''It validates The Single Unix Specification: "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." (see [[http://www.opengroup.org/onlinepubs/009695399/utilities/printf.html|printf()]] to know more) -- mjf''
Some versions avoiding a subshell:
{{{
oldchr () { printf \\$(printf '%03o' $1) ;}

#posix
chr () {
    set -- $(($1 / 64)) $(($1 % 64))
    set -- $1 $(($2 / 8)) $(($2 % 8))
    printf \\"${1}${2}${3}"
}

#bash only
chr_bash () {
    local temp
    printf -v temp '%03o' $1
    printf \\$temp
}

#test
for i in {1..255} ;do [[ "$(oldchr $i)" = "$(chr $i)" ]] || echo $i;done
for i in {1..255} ;do [[ "$(oldchr $i)" = "$(chr_bash $i)" ]] || echo $i;done
for p in oldchr chr chr_bash; do echo $p:;time for i in {1..4000}; do $p 65 >/dev/null; done; done

}}}
the timings:
{{{
$ bash chr
oldchr:

real 0m14.350s
user 0m5.004s
sys 0m9.248s
chr:

real 0m0.422s
user 0m0.059s
sys 0m0.216s
chr_bash:

real 0m0.400s
user 0m0.042s
sys 0m0.189s

}}}

Yet another version probably faster:
{{{
chr () {
    printf \\$(($1/64*100+$1%64/8*10+$1%8))
}


}}}

How do I convert an ASCII character to its decimal (or hexadecimal) value and back?

If you have a known octal or hexadecimal value (at script-writing time), you can just use printf:

   # POSIX
   printf '\x27\047\n'

This prints two literal ' characters (27 is the hexadecimal ASCII value of the character, and 47 is the octal value) and a newline.

If you need to convert characters (or numeric ASCII values) that are not known in advance (i.e., in variables), you can use something a little more complicated:

   # POSIX
   # chr() - converts decimal value to its ASCII character representation
   # ord() - converts ASCII character to its decimal value

   chr() {
     printf \\$(printf '%03o' $1)
   }

   ord() {
     printf '%d' "'$1"
   }

   # hex() - converts ASCII character to a hexadecimal value
   # unhex() - converts a hexadecimal value to an ASCII character

   hex() {
      printf '%x' "'$1"
   }

   unhex() {
      printf \\x"$1"
   }

   # examples:

   chr $(ord A)    # -> A
   ord $(chr 65)   # -> 65

The ord function above is quite tricky.

  • Tricky? Rather, it's using a feature that I can't find documented anywhere -- putting a single quote in front of an integer. Neat effect, but how on earth did you find out about it? Source diving? -- GreyCat

    • It validates The Single Unix Specification: "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." (see printf() to know more) -- mjf

Some versions avoiding a subshell:

oldchr () {  printf \\$(printf '%03o' $1) ;}

#posix
chr () {
    set -- $(($1 / 64)) $(($1 % 64))
    set -- $1  $(($2 / 8)) $(($2 % 8))
    printf \\"${1}${2}${3}"
}

#bash only
chr_bash () {
    local temp
    printf -v temp  '%03o' $1
    printf \\$temp
}

#test
for i in {1..255} ;do [[ "$(oldchr $i)" = "$(chr $i)" ]] || echo $i;done
for i in {1..255} ;do [[ "$(oldchr $i)" = "$(chr_bash $i)" ]] || echo $i;done
for p in oldchr chr chr_bash; do echo $p:;time for i in {1..4000}; do $p 65 >/dev/null; done; done

the timings:

$ bash  chr
oldchr:

real    0m14.350s
user    0m5.004s
sys     0m9.248s
chr:

real    0m0.422s
user    0m0.059s
sys     0m0.216s
chr_bash:

real    0m0.400s
user    0m0.042s
sys     0m0.189s

Yet another version probably faster:

chr () {
    printf \\$(($1/64*100+$1%64/8*10+$1%8))
}

BashFAQ/071 (last edited 2021-02-08 16:03:51 by GreyCat)