3

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.

2

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
1

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
  • 1
    This inelegant hack works! Much appreciated! – Jcwilli585 Aug 5 '15 at 21:31
  • 1
    @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
1
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.
  • Please edit your answer to add a few words of explanation. – Scott Mar 28 '17 at 18:28
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.