How to convert hex to ASCII characters in the Linux shell?
Asked Answered
T

14

77

Let's say that I have a string 5a.

This is the hex representation of the ASCII letter Z.

I need to find a Linux shell command which will take a hex string and output the ASCII characters that the hex string represents.

So if I do:

echo 5a | command_im_looking_for

I will see a solitary letter Z:

Z
Tonguing answered 22/10, 2009 at 2:52 Comment(0)
J
63
echo -n 5a | perl -pe 's/([0-9a-f]{2})/chr hex $1/gie'

Note that this won't skip non-hex characters. If you want just the hex (no whitespace from the original string etc):

echo 5a | perl -ne 's/([0-9a-f]{2})/print chr hex $1/gie'

Also, zsh and bash support this natively in echo:

echo -e '\x5a'
Judicator answered 22/10, 2009 at 2:55 Comment(10)
You probably need to put an "i" after the regexes there.Ineluctable
Or put a-fA-F in the character class.Ineluctable
I'm not normally a fan of perl, but this is the sort of stuff that it does well. If you're going to be converting a very large amount of data, though, it would be better to write a short C program to do this.Migration
I don't think you will see much benefit from doing it in C until you have to deal with terabytes of data.Ineluctable
@Kinopiko, it processes about 1.5 MB/s (output) here, so really, by the time you get to hundreds of megabytes, it's time to write something in a faster language :)Judicator
Much easier to use perl -lne 'print pack "H*", $_'.Wommera
@Randal, it's easier for me to use this one because I don't need to perldoc -f pack each time ;) of course, YMMV...Judicator
echo -ne '\x5a' is necessary, given the example: without the -n switch for echo, a new line ('\x0a') will be output after the Z.Goudy
as a side note to what @Goudy said, one could test if everything's right via echo -ne '\x5a\xde\x0b\x0d' | xxdDiaphoretic
I had a huge hex string generated from a mysqldump hex-blob saved to a variable. I used a modified form of the above to output the original file. I used echo -e "$(echo $hex | sed 's_\(..\)_\\x\1_g')". Just saw the next post using xxd. echo $hex | xxd -r -p worked like a charm.Deliberate
C
117

I used to do this with xxd:

echo -n 5a | xxd -r -p

But then I realised that in Debian/Ubuntu, xxd is part of vim-common and hence might not be present in a minimal system. To also avoid Perl (IMHO also not part of a minimal system), I ended up using sed, xargs, and printf like this:

echo -n 5a | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf

Mostly, I only want to convert a few bytes and it's okay for such tasks. The advantage of this solution over the one of ghostdog74 is, that this can convert hex strings of arbitrary lengths automatically. xargs is used because printf doesnt read from standard input.

Ceresin answered 6/10, 2011 at 22:54 Comment(5)
Damn, i know it's 3 years later on the orignial answer, but I had no clue xxd existed. With the issue i'm having now, i have continual need for it... constant hex dumps of byte strings from a debugger. And yeah, @Bantamweight comment is damn pointless.Afferent
Instead of "echo -n 5a | xxd -r -p" I'd prefer to use "xxd -r -p <<< 5a". Advantages: (1) a few less characters to type (typing also faster because of 3x repetition); (2) runs only one command (saves the echo), not two => faster execution.Natterjack
@KurtPfeifle sh: 1: Syntax error: redirection unexpected disadvantage: not POSIXCeresin
@josch: Sorry, I should have mentioned it needs Bash to work. It is a here string in Bash (a concept similar to a here document).Natterjack
xxd -r not available in busybox, but the second one is fine. thanksSelfassurance
J
63
echo -n 5a | perl -pe 's/([0-9a-f]{2})/chr hex $1/gie'

Note that this won't skip non-hex characters. If you want just the hex (no whitespace from the original string etc):

echo 5a | perl -ne 's/([0-9a-f]{2})/print chr hex $1/gie'

Also, zsh and bash support this natively in echo:

echo -e '\x5a'
Judicator answered 22/10, 2009 at 2:55 Comment(10)
You probably need to put an "i" after the regexes there.Ineluctable
Or put a-fA-F in the character class.Ineluctable
I'm not normally a fan of perl, but this is the sort of stuff that it does well. If you're going to be converting a very large amount of data, though, it would be better to write a short C program to do this.Migration
I don't think you will see much benefit from doing it in C until you have to deal with terabytes of data.Ineluctable
@Kinopiko, it processes about 1.5 MB/s (output) here, so really, by the time you get to hundreds of megabytes, it's time to write something in a faster language :)Judicator
Much easier to use perl -lne 'print pack "H*", $_'.Wommera
@Randal, it's easier for me to use this one because I don't need to perldoc -f pack each time ;) of course, YMMV...Judicator
echo -ne '\x5a' is necessary, given the example: without the -n switch for echo, a new line ('\x0a') will be output after the Z.Goudy
as a side note to what @Goudy said, one could test if everything's right via echo -ne '\x5a\xde\x0b\x0d' | xxdDiaphoretic
I had a huge hex string generated from a mysqldump hex-blob saved to a variable. I used a modified form of the above to output the original file. I used echo -e "$(echo $hex | sed 's_\(..\)_\\x\1_g')". Just saw the next post using xxd. echo $hex | xxd -r -p worked like a charm.Deliberate
I
16

You can do this with echo only, without the other stuff. Don't forget to add "-n" or you will get a linebreak automatically:

echo -n -e "\x5a"
Inoculation answered 31/10, 2012 at 14:54 Comment(2)
Doesn't answer the question asked above, but answers the question I was searching for when google send me here :-) (btw. sending the output to | hexdump -C helps to identify unwanted newlines etc.)Caramel
This requires that \x be inserted before each hex pair. Example was 5a, but question was about converting a hex string to multiple ASCII characters.Torrie
R
10

Bash one-liner

echo -n "5a" | while read -N2 code; do printf "\x$code"; done
Resurgent answered 29/9, 2014 at 7:37 Comment(2)
This doesn't seem to work for multi-byte messages... When I run echo -n "0102abcd" | while read -n 2 code; do printf "\x$code"; done | hexdump yields 0201 cdabWhipstitch
@John Steel It seems hexdump displays data as numeric words in hex by default. Try this echo -n "0102abcd" | while read -n 2 code; do printf "\x$code"; done | hexdump -e '/1 "%02x"'Resurgent
S
5

Some Python 3 one-liners that work with any number of bytes.

Decoding hex

Using strip, so that it's ok to have a newline on stdin.

$ echo 666f6f0a | python3 -c "import sys, binascii; sys.stdout.buffer.write(binascii.unhexlify(input().strip()))"
foo

Encoding hex

$ echo foo | python3 -c "import sys, binascii; print(binascii.hexlify(sys.stdin.buffer.read()).decode())"
666f6f0a
Scherzando answered 14/10, 2015 at 15:15 Comment(2)
Just use print when decoding hex and you can see ctrl chars, null chars, e.t.c.. e.g. 'echo 666f6f0a62617200 | python3 -c "import sys, binascii; print(binascii.unhexlify(input()))"' OUTPUT: 'b'foo\nbar\x00''Weigh
You can shorten this to: python3 -c 'import binascii; print(binascii.unhexlify(input()))' <<< 486921Ledge
A
4

Depending on where you got that "5a", you can just prepend "\x" to it and pass that to printf:

$ a=5a
$ a="\x${a}"
$ printf "$a"
Z
Apheliotropic answered 22/10, 2009 at 3:31 Comment(1)
The second and third lines can be collapsed into one: printf "\x${a}"Faraday
S
2
echo 5a | python -c "import sys; print chr(int(sys.stdin.read(),base=16))"
Stowage answered 22/10, 2009 at 3:4 Comment(2)
Change the print to sys.stdout.write to prevent the extra linefeed character.Postulate
OverflowError: Python int too large to convert to C longBlinders
B
2

Here is a pure bash script (as printf is a bash builtin) :

#warning : spaces do matter
die(){ echo "$@" >&2;exit 1;}

p=48656c6c6f0a

test $((${#p} & 1)) == 0 || die "length is odd"
p2=''; for ((i=0; i<${#p}; i+=2));do p2=$p2\\x${p:$i:2};done
printf "$p2"

If bash is already running, this should be faster than any other solution which is launching a new process.

Bantamweight answered 8/9, 2014 at 12:41 Comment(1)
slightly shorter: for ((i=0; i<${#p}; i+=2));do p2+=\\x${p:$i:2};doneAthallia
S
2

dc can convert between numeric bases:

$ echo 5a | (echo 16i; tr 'a-z' 'A-Z'; echo P) | dc
Z$
Stoffel answered 30/12, 2014 at 20:7 Comment(0)
H
2

There is a simple shell command ascii.

If you use Ubuntu, install it with:

sudo apt install ascii

Then

ascii 0x5a

will output:

ASCII 5/10 is decimal 090, hex 5a, octal 132, bits 01011010: prints as `Z'
Official name: Majuscule Z
Other names: Capital Z, Uppercase Z
Haematin answered 23/7, 2020 at 12:59 Comment(0)
B
1

As per @Randal comment, you can use perl, e.g.

$ printf 5a5a5a5a | perl -lne 'print pack "H*", $_'
ZZZZ

and other way round:

$ printf ZZZZ | perl -lne 'print unpack "H*", $_'
5a5a5a5a

Another example with file:

$ printf 5a5a5a5a | perl -lne 'print pack "H*", $_' > file.bin
$ perl -lne 'print unpack "H*", $_' < file.bin
5a5a5a5a
Botulism answered 12/7, 2018 at 21:40 Comment(0)
G
1

You can use this command (python script) for larger inputs:

echo 58595a | python -c "import sys; import binascii; print(binascii.unhexlify(sys.stdin.read().strip()).decode())"

The result will be:

XYZ

And for more simplicity, define an alias:

alias hexdecoder='python -c "import sys; import binascii; print(binascii.unhexlify(sys.stdin.read().strip()).decode())"'

echo 58595a | hexdecoder
Gamekeeper answered 2/10, 2020 at 10:58 Comment(0)
V
0

GNU awk 4.1

awk -niord '$0=chr("0x"RT)' RS=.. ORS=

Note that if you echo to this it will produce an extra null byte

$ echo 595a | awk -niord '$0=chr("0x"RT)' RS=.. ORS= | od -tx1c
0000000  59  5a  00
          Y   Z  \0

Instead use printf

$ printf 595a | awk -niord '$0=chr("0x"RT)' RS=.. ORS= | od -tx1c
0000000  59  5a
          Y   Z

Also note that GNU awk produces UTF-8 by default

$ printf a1 | awk -niord '$0=chr("0x"RT)' RS=.. ORS= | od -tx1
0000000 c2 a1

If you are dealing with characters outside of ASCII, and you are going to be Base64 encoding the resultant string, you can disable UTF-8 with -b

echo 5a | sha256sum | awk -bniord 'RT~/\w/,$0=chr("0x"RT)' RS=.. ORS=
Vyatka answered 11/5, 2014 at 22:37 Comment(0)
B
0

Similar to my answer here: Linux shell scripting: hex number to binary string

You can do it with the same tool like this (using ascii printable character instead of 5a):

echo -n 616263 | cryptocli dd -decoders hex

Will produce the following result:

abcd
Brahma answered 12/11, 2017 at 19:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.