If you want to go from the character code to the character itself, you include the character code in the printf format string, escaped with a backslash, in octal.
E.g. printf "\044\n" prints $ (and a newline).
In Bash and other shells, you could use hex, \x24, but that's not standard and doesn't work in Dash.
You could nest another printf in a command substitution to convert from hex or decimal to octal, though. Both of these would print $ (and a newline):
printf "\\$(printf %o 36)\n"
printf "\\$(printf %o 0x24)\n"
Answer from ilkkachu on Stack Exchangebash - Using `printf` to output chars given some ASCII numbers - Unix & Linux Stack Exchange
shell - How do I print an ASCII character by different code points in Bash? - Unix & Linux Stack Exchange
How to convert a Hex char buffer to an ASCII-string [C] - Stack Overflow
c - Hex to ascii string conversion - Stack Overflow
In printf in the bash manual, you'll find:
Arguments to non-string format specifiers are treated as C language constants, except that a leading plus or minus sign is allowed, and if the leading character is a single or double quote, the value is the ASCII value of the following character.
(my emphasis)
These shell functions encapsulate char_to_hex and hex_to_char. Function names stolen from perl
# ord: the ascii value of a character
# $ ord "A" #=> 65
#
ord() {
printf "%d" "\"$1"
}
# chr: the character represented by the given ASCII decimal value
# $ chr 65 #=> A
#
chr() {
printf "\x$(printf "%x" "$1")"
}
Then:
printf '%02X\n' "$(ord A)"
41
Though short, @Juergen comments that this answers his question. Use the following:
printf "%X\n" \"A\"
Note, this provides the single-byte ("ASCII", UTF-8) charcter code. See the Unix & Linux StackExchange for more on char -> hex value and the converse, hex -> char, using bash shell and/or python.
Oh, sure, just that it has to be done in two steps. Like a two step tango:
$ printf "$(printf '\\x%s' 48 65 6c 6c 6f)"; echo
Hello
Or, alternatively:
$ test () { printf "$(printf '\\x%s' "$@")"; echo; }
$ test 48 65 6c 6c 6f
Hello
Or, to avoid printing on "no arguments":
$ test () { [ "$#" -gt 0 ] && printf "$(printf '\\x%s' "$@")"; echo; }
$ test 48 65 6c 6c 6f
Hello
$
That is assuming that the arguments are decimal values between 1 and 127 (empty arguments would be counted but will fail on printing).
\x needs to be followed by a literal hexadecimal value:
$ printf '\x48\n'
H
printf '\x%s\n' "$c"
bash: printf: missing hex digit for \x
\x48
Presumably this is because printf will expand any hexadecimal literals in the format string as a separate step before using the resulting string as the format.
What you can do instead:
unhexlify() {
for character
do
format="\x${character}"
printf "$format"
done
printf '\n'
}
Test:
$ unhexlify 48 65 6c 6c 6f
Hello
Hex:
printf '\x4a'
Dec:
printf "\\$(printf %o 74)"
Alternative for hex :-)
xxd -r <<<'0 4a'
In general, the shell could understand hex, oct and decimal numbers in variables, provided they have been defined as integers:
$ declare -i v1 v2 v3 v4 v5 v6 v7
$ v1=0112
$ v2=74
$ v3=0x4a
$ v4=8#112
$ v5=10#74
$ v6=16#4a
$ v7=18#gg
echo "$v1 $v2 $v3 $v4 $v5 $v6 $v7"
74 74 74 74 74 74 304
Or they are the result of an "Arithmetic Expansion":
$ : $(( v1=0112, v2=74, v3=0x4a, v4=8#112, v5=10#74, v6=16#4a, v7=18#gg ))
$ echo "$v1 $v2 $v3 $v4 $v5 $v6 $v7"
74 74 74 74 74 74 304
So, you just need one way to print the character that belongs to a variable value.
But here are two possible ways:
$ var=$((0x65))
$ printf '%b\n' "\\$(printf '0%o' "$var")"
e
$ declare -i var
$ var=0x65; printf '%b\n' "\U$(printf '%08x' "$var")"
e
The two printf are needed, one to transform the value into an hexadecimal string and the second to actually print the character.
The second one will print any UNICODE point (if your console is correctly set).
For example:
$ var=0x2603; printf '%b\n' "\U$(printf '%08x' "$var")"
☃
An snow man.
The character that has an utf-8 representation as f0 9f 90 ae is 0x1F42E.
Search for cow face site:fileformat.info to get it:
$ var=0x1F42F; printf '%b\n' "\U$(printf '%08x' "$var")"
🐮
Note: There is a problem with the UNICODE way in that for bash before 4.3 (corrected in that version and upwards), the characters between UNICODE points 128 and 255 (in decimal) may be incorrectly printed.
References
Fourth paragraph inside PARAMETERS in man bash:
If the variable has its integer attribute set, then value is evaluated as an arithmetic expression even if the $((...)) expansion is not used (see Arithmetic Expansion below).
Inside "ARITHMETIC EVALUATION" in man bash:
Constants with a leading 0 are interpreted as octal numbers. A leading 0x or 0X denotes hexadecimal. Otherwise, numbers take the form [base#]n, where the optional base is a decimal number between 2 and 64 representing the arithmetic base, and n is a number in that base. If base# is omitted, then base 10 is used. The digits greater than 9 are represented by the lowercase letters, the uppercase letters, @, and _, in that order. If base is less than or equal to 36, lowercase and uppercase letters may be used interchangeably to represent numbers between 10 and 35.
Okay, let's have a shot:
#include <ctype.h>
#include <stdio.h>
static unsigned char hexdigit2int(unsigned char xd)
{
if (xd <= '9')
return xd - '0';
xd = tolower(xd);
if (xd == 'a')
return 10;
if (xd == 'b')
return 11;
if (xd == 'c')
return 12;
if (xd == 'd')
return 13;
if (xd == 'e')
return 14;
if (xd == 'f')
return 15;
return 0;
}
int main(void)
{
const char st[] = "48656C6C6F3B", *src = st;
char text[sizeof st + 1], *dst = text;
while (*src != '\0')
{
const unsigned char high = hexdigit2int(*src++);
const unsigned char low = hexdigit2int(*src++);
*dst++ = (high << 4) | low;
}
*dst = '\0';
printf("Converted '%s', got '%s'\n", st, text);
return 0;
}
The very verbose-looking digit2int() function is trying to not be ASCII-dependent, which is always nice. Note that the loop in main() assumes proper input, it only checks for termination every two characters and won't handle non-hex data.
Oh, and this prints:
Converted '48656C6C6F3B', got 'Hello;'
as you probably know each character is represented by an int --> ...'a' = 97, 'b'=98... so once you convert an hex to int there is no need to create another function converting it to ascii - just by storing the integer into a character will do the trick (unless I'm missing something understanding the exercise).
as for storing it back to memory: there are many options:
1. remove the const before const char st[12] = "48656C6C6F3B"; and
assign the return value from the function to the desired cell or using sscanf
take a look here
you need to take 2 (hex) chars at the same time... then calculate the int value and after that make the char conversion like...
char d = (char)intValue;
do this for every 2chars in the hex string
this works if the string chars are only 0-9A-F:
#include <stdio.h>
#include <string.h>
int hex_to_int(char c){
int first = c / 16 - 3;
int second = c % 16;
int result = first*10 + second;
if(result > 9) result--;
return result;
}
int hex_to_ascii(char c, char d){
int high = hex_to_int(c) * 16;
int low = hex_to_int(d);
return high+low;
}
int main(){
const char* st = "48656C6C6F3B";
int length = strlen(st);
int i;
char buf = 0;
for(i = 0; i < length; i++){
if(i % 2 != 0){
printf("%c", hex_to_ascii(buf, st[i]));
}else{
buf = st[i];
}
}
}
Few characters like alphabets i-o couldn't be converted into respective ASCII chars . like in string '6631653064316f30723161' corresponds to fedora . but it gives fedra
Just modify hex_to_int() function a little and it will work for all characters. modified function is
int hex_to_int(char c)
{
if (c >= 97)
c = c - 32;
int first = c / 16 - 3;
int second = c % 16;
int result = first * 10 + second;
if (result > 9) result--;
return result;
}
Now try it will work for all characters.
The reason is because hexdump by default prints out 16-bit integers, not bytes. If your system has them, hd (or hexdump -C) or xxd will provide less surprising outputs - if not, od -t x1 is a POSIX-standard way to get byte-by-byte hex output. You can use od -t x1c to show both the byte hex values and the corresponding letters.
If you have xxd (which ships with vim), you can use xxd -r to convert back from hex (from the same format xxd produces). If you just have plain hex (just the '4161', which is produced by xxd -p) you can use xxd -r -p to convert back.
For the first part, try
echo Aa | od -t x1
It prints byte-by-byte
$ echo Aa | od -t x1
0000000 41 61 0a
0000003
The 0a is the implicit newline that echo produces.
Use echo -n or printf instead.
$ printf Aa | od -t x1
0000000 41 61
0000002
you have to use "%X" in the printf():
#include <stdio.h>
#include <string.h>
unsigned char global_buffer[5]={0x0A,0x21,0x01,0x01,0x01};
main()
{
int i;
for(i=0;i<5;i++) {
printf("%X, ", global_buffer[i]);
}
printf("\n");
}
Use the %x and %X for printing hexadecimal values. consider the example
int hex = 0X0A;
printf("%x\n",hex); o/p a //for small letters
printf("%X" , hex); o/p A // for capital letters