5

I've been trying to make printf output some chars, given their ASCII numbers (in hex)... something like this:

#!/bin/bash

hexchars () { printf '\x%s' $@ ;}

hexchars 48 65 6c 6c 6f

Expected output:
Hello

For some reason that doesn't work though. Any ideas?

EDIT:

Based on the answer provided by Isaac (accepted answer), I ended up with this function:

chr  () { local var ; printf -v var '\\x%x' $@ ; printf "$var" ;}

Note, I rewrote his answer a bit in order to improve speed by avoiding the subshell.

Result:

~# chr 0x48 0x65 0x6c 0x6c 0x6f
Hello
~# chr 72 101 108 108 111
Hello
~# chr {32..126}
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}

I guess the inverse of the chr function would be a function like...

asc () { printf '%d\n' "'$1"  ;}
asc A
65
chr 65
A

Or, if we want strictly hex variants...

chrx () { local var ; printf -v var '\\x%s' $@ ; printf "$var\n" ;}
ascx () { printf '%x\n' "'$1"  ;}

chrx 48 65 6c 6c 6f
Hello
ascx A
41

Thank you!

5
  • 1
    What do you mean by "it doesn't work"? Commented Mar 21, 2021 at 3:46
  • What do you mean by "what do you mean"? Commented Mar 21, 2021 at 9:22
  • 2
    It means: Did it output nothing, output something you can't understand, crash, or turn into a bowl of petunias? I don't usually take my Volvo in for a service and tell the mechanic "It doesn't work". I try to tell him the symptoms, so he does not charge me for balancing the wheels because I couldn't retune the radio. Commented Mar 21, 2021 at 11:09
  • You should use "$@" not the unqouted one. That is wrong. Commented Mar 21, 2021 at 15:47
  • Since you are getting into more details, you need to read : mywiki.wooledge.org/BashFAQ/071 Commented Mar 21, 2021 at 15:48

3 Answers 3

5

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

3
  • Thank you Isaac! Accepted answer. Commented Mar 21, 2021 at 6:59
  • Sure, YW. @patilan Commented Mar 21, 2021 at 7:21
  • None of your proposals works as claimed if you are using a standard compliant printf. Commented May 24, 2021 at 11:37
2

\x needs to be followed by a literal hexadecimal value:

$ printf '\x48\n'
H
$ c=48
$ 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
4
  • +1. Although, I was so hoping that the OP's one-liner could be somehow made to work. Commented Mar 21, 2021 at 4:30
  • This does not work, since printf does not expand hex excapes. The printf built into bash however may do... Commented May 24, 2021 at 11:39
  • @schily It does work with the printf built into Bash, which I assume the question was about. Commented May 24, 2021 at 19:58
  • People who read this may not understand this. This is why I in my answers always mention it when a specific feature is non-standard. Commented May 24, 2021 at 20:21
2

Using perl and it's pack function we pack two hex digits and print the ASCII representation.

charx() {
  perl -le 'print pack "(H2)*", @ARGV' -- "$@"
}

We use the desk calculator dc to input base 16 and print out in their ASCII characters using the a command.

charx() {
dc <<eof
16i
$(echo "$@" | tr a-f A-F)
[SAz0<a]sa
[LAanln1-dsn0<b]sb
[zsnlaxlbxAan]sc
z0<c
eof
}

Used as:

charx  48 65 6c 6c 6f

Result

 Hello

You must log in to answer this question.