# Using awk to store a binary number in text file as string and then convert to hexadecimal

I'm using `awk` to parse a text file. The text file has four fields of byte data and appears as such:

``````11110000  10100000  10110000  10010000
``````

I want to read the fields and convert them from binary to a hexadecimal. Currently I'm using `printf %x`. This way changes `11110000` into a decimal and then into a hex number. This says the value of `1111000` is `0xA98760` instead of `0xF0`.

My code is simple and I'm new to BASH and Linux.

``````awk'{printf(%x %x %x %x, \$1, \$2, \$3, \$4)};
``````

How can I store the string fields as the binary that it is and then convert to Hex? I can get the hex number from the terminal using " bc <<< "obase=16;ibase=2; \$variable"". When I try to script this I get a syntax error.

Its an inelegant hack, but it works. Declares a function b which takes a binary representation of a number and returns the decimal value. Then relies on `printf` and its %x to show in hex.

``````\$ awk 'func b(i, t,a,c){a=1;for(c=length(i);c>0;c--){t+=substr(i,c,1)=="1"?a:0;a*=2}return t}{printf "0x%x 0x%x 0x%x 0x%x\n",b(\$1),b(\$2),b(\$3),b(\$4)}' bin.txt
0xf0 0xa0 0xb0 0x90
``````
• This inelegant hack works! Much appreciated! – Jcwilli585 Aug 5 '15 at 21:31
• @Jcwilli585 If this answer solved your issue, please take a moment and accept it by clicking on the check mark to the left. That will mark the question as answered and is the way thanks are expressed on the Stack Exchange sites. – terdon Aug 5 '15 at 22:06

`awk` only does decimal, octal and hexadecimal, not binary. You could use `perl` instead:

``````perl -lane 'BEGIN{\$, = " "} print unpack "(H2)*", pack("(B8)4", @F)'
``````

With `dc` and GNU `tac` (see also `tail -r` on some systems):

``````{ echo 16o2i; cat; echo f; } < file.txt | dc | tac
``````

With `bc` (assuming your syntax error was about the `<<<` `zsh` operator used in a shell other than `zsh` or recent versions of `bash`/`ksh93`/`mksh`/`yash`)

``````{ echo 'obase=16; ibase=2'; tr -cs 01 '[\n*]'; } < file.txt | bc
``````
• I don't have Perl and there's no way to add it to the system I'm using. I can get the hex number from the terminal using " bc <<< "obase=16;ibase=2; \$variable"". When I try to script this I get a syntax error. – Jcwilli585 Aug 3 '15 at 21:47
• @Jcwilli585 add that update to your question and you might get an answer for that part too – roaima Aug 3 '15 at 22:08
• @Jcwilli585 you don't have Perl? I have my doubts – Neil McGuigan Mar 29 '17 at 18:35
``````perl -pale '\$_ = join \$", map { sprintf "%X", oct "0b\$_" } @F'
``````

# Brief:

``````Perl options:
-p => autoprint + implcit file read in/line, a.la., awk
-a => autosplit line into fields using default delimiter ' '
-l => ORS=IRS="\n"

Perl standard variables:
\$" => list separator, by default it is a single space " "
\$_ => refers to the whole line, equivalent to \$0 in awk
@F => fields got by splitting the current line, \$1, \$2,\$3, ... \$NF in awk

Perl code:
map { body } list_of_items
Map takes in an input list and applies the code present in
it's body on each element of this list to generate an output list.
``````
``````      join <separator>, <list>
Join takes in an input list and joins their elements by the
separator provided in the first argument.

oct <number>
When the input to this function is prefixed by a "0b", then
it is treated to be a binary number and outputs the decimal
equivalent.

sprintf "%x" <decimal>
This function whose format is "%x" treats the input as
decimal and returns the equivalent number in hex.
``````

If the system you're targetting has GNU Bash, you can take advantage of its support, in it arithmetic expressions, for bases other than just octal and hex. For instance:

``````bash\$ echo \$(( 2#1011 | 2#1100 ))   # bitwise OR of 11 and 12
15
``````

Okay, so if we take this data:

``````11110000  10100000  10110000  10010000
``````

and somehow massage it into this syntax and evaluate it:

``````\$(( 2#11110000101000001011000010010000 ))
``````

we get the value

``````4037062800
``````

(Yes, this works on 32 bit builds of bash; we don't get a negative.)

We can do this very simply:

``````echo \$(( 2#\$(tr -d ' ' < file) ))
4037062800
``````

The only problem is that this is, to an extent, `eval` in disguise. We are interpolating the output of some command, based on the contents of a file, into an expression which then gets evaluated. If `file` is obtained from an untrusted source, there could be security implications. For instance, I wouldn't put this into a CGI script under Apache, where `file` comes the POST data of a form submitted by arbitrary Internet users.