Search code examples
bashcygwin

Is there a Cygwin Bash command or script capability to convert a hex string to packed binary?


For work, I'm writing a Cygwin Bash script that must convert a hex string to packed binary format. At the present, I'm doing this with a C# program. But I would really like to do it inside the script, if possible. Is there a command that will do this? If not, is it feasible to do it with script programming?

For reference, I've included my C# program below. It was just something I wrote for quick testing, not as serious program. But it shows what I need done. I need the hex digits converted to binary (4 bits each), then packed two per byte.

using System;
using System.IO;

namespace HexAsciiToPackedBinary {

   class Program {

        static void Main(string[] args) {

            if (args.Length != 2) {
                Console.WriteLine("usage: ha2pb infile outfile");
                return;
            }

            string infile = args[0];
            string outfile = args[1];

            byte[] allBytes = File.ReadAllBytes(infile); 
            byte[] packedBytes = new byte[allBytes.Length / 2];

            for (int i = 0; i < allBytes.Length; i += 2) {
                byte ubits = (byte)(GetVal(allBytes[i]) << 4);
                byte lbits = GetVal(allBytes[i + 1]); 
                packedBytes[i/2] = (byte)(ubits | lbits);
            }

            File.WriteAllBytes(outfile, packedBytes);
        }

        static byte GetVal(byte b) {
         if (b >= 0x30 && b <= 0x39) { // 0 - 9
            return (byte)(b - 0x30);
         }
         if (b >= 0x61 && b <= 0x66) { // a - f
            return (byte)(b - 0x61 + 10);
         }
         if (b >= 0x41 && b <= 0x46) { // A - F
            return (byte)(b - 0x41 + 10);
         }
         Console.WriteLine("Bad char.");
         return unchecked((byte)-1);
        }
    }
}

Solution

  • If the hex string comes from a bash variable:

    $ str='415A41'
    $ for((i=0; i<${#str}; i+=2)); do printf "\x${str:i:2}"; done
    AZA
    

    If it comes from a text file:

    $ { echo "414141"; echo "5A5A5A"; } > file.hex
    $ mapfile -t a < file.hex
    $ for((i=0; i<${#a[@]}; i++)); do for((j=0; j<${#a[i]}; j+=2)); do
        printf "\x${a[i]:j:2}"
      done; done
    AAAZZZ
    

    Note that, if you have it installed, xxd has a revert mode that you could maybe use. But we would need more information about your input format. Example:

    $ echo '0x4141415A5A5A' | xxd -rp
    AAAZZZ