0

I am using bc to convert a long hex vectors to binary. It does not work for the following code example in my awk script:

#!/bin/awk -f

cmd = "bc <<< \"ibase=16;obase=2;"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"\""

result = ((cmd | getline binary_vector) > 0 ? binary_vector : -1)
close(cmd)
print "conversion result: " result

I get the following as output:

conversion result: 11111111111111111111111111111111111111111111111111111111111111111111\

This is not the complete result. bc does not convert the whole string and truncates in between with a "\". How can I convert the whole hexadecimal number?

Antoine
  • 800
  • 3
  • 14
  • 29
valar_m
  • 35
  • 1
  • 5
  • Why is that `FFF` value unquoted? Should that not be inside the awk quoted string? Also that's not a valid awk program. Is `csh` involved here at all? – Etan Reisner Jun 11 '15 at 13:34
  • Isn't the problem here that `bc` is outputting more than one line but you are only reading one line? – Etan Reisner Jun 11 '15 at 13:36
  • This is a working awk script which I call from a csh script. I just replaced the column number in my awk script with a constant hex value to build this example. The script works for lets says, 64-bit hex input. – valar_m Jun 11 '15 at 13:42
  • 1
    `cmd = "BC_LINE_LENGTH=0 bc <<< \"ibas ...\"` – Phylogenesis Jun 11 '15 at 13:43
  • I do not at all see how that can possibly work. That's using an undefined awk variable and is otherwise an awk syntax error as far as I can see. What awk are you using? Anyway, I believe your problem is just that you need to `getline` more than one line of output from `bc`. Try running that `bc` command by hand and you'll see that it spits out multiple lines of output (hence the trailing backslash in your result). – Etan Reisner Jun 11 '15 at 13:44
  • @Phylogenesis Nice. I was just about to go look for something like that. – Etan Reisner Jun 11 '15 at 13:44
  • BC_LINE_LENGTH=0 does not change the output – valar_m Jun 11 '15 at 13:51
  • Thanks. Can you tell me how to get multiple lines from bc. Doing a "getline binary_vector" again in the code gives me nothing – valar_m Jun 11 '15 at 14:30

2 Answers2

0

The following command makes bc return a binary string of 1000 characters in one line.

cmd = "BC_LINE_LENGTH=1000 bc <<< \"ibas ...\"
valar_m
  • 35
  • 1
  • 5
  • This should be an integer specifying the number of characters in an output line for numbers. This includes the backslash and newline characters for long numbers. As an extension, the value of zero disables the multi-line feature. Any other value of this variable that is less than 3 sets the line length to 70. – Phylogenesis Jun 11 '15 at 15:18
  • I tried with BC_LINE_LENGTH=0. It does not disable the feature. Line length stays to 70 – valar_m Jun 11 '15 at 15:21
  • Have you checked `man bc` to see what your version says about that environment variable? – Phylogenesis Jun 11 '15 at 15:26
  • According to [this bug report](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=245899), setting `BC_LINE_LENGTH=0` to mean no line breaks was added in version 1.06.94-1 of bc. – Phylogenesis Jun 11 '15 at 15:33
  • I might be having an old version. Thank you for your help. – valar_m Jun 11 '15 at 15:33
0

Here's an awk script I modified from a previous answer.

#!/usr/bin/awk -f

{
    cnt = split($0, a, FS)
    if( convertArrayBase(16, 2, a, cnt) > -1 ) {

        # use the array here
        for(i=1; i<=cnt; i++) {
            print a[i]
        }
    }
}

    # Destructively updates input array, converting numbers from ibase to obase
    #
    # @ibase:  ibase value for bc
    # @obase:  obase value for bc
    # @a:      a split() type associative array where keys are numeric
    # @cnt:    size of a ( number of fields )
    #
    # @return: -1 if there's a getline error, else cnt
    #
function convertArrayBase(ibase, obase, a, cnt,         i, b, c, cmd) {

    cmd = sprintf("echo \"ibase=%d;obase=%d", ibase, obase)
    for(i=1; i<=cnt; i++ ) {
        cmd = cmd ";" a[i]
    }
    cmd = cmd "\" | bc"

    i = 0 # reset i
    while( (cmd | getline b) > 0 ) {
        # if the return from bc is long, check for "\$" and build up c
        if( b ~ /\\$/ ) {
            sub(/\\/, "", b)
            c = c b
            continue;
        }
        a[++i] = c ? c b : b
        c = ""
    }
    close( cmd )
    return i==cnt ? cnt : -1
}

when the script is called awko and chmod +x awko it can be run like:

$ echo "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" | ./awko
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

The script attempts to convert the base of all the fields in a record, so the following also works:

$ echo "A B C D E F" | ./awko
1010
1011
1100
1101
1110
1111

Notice that the record's fields are output as separate lines. Instead of echo, an input file can be given to the script.

Community
  • 1
  • 1
n0741337
  • 2,474
  • 2
  • 15
  • 15