47

Which is a good tool to convert ASCII to binary, and binary to ASCII?

I was hoping for something like:

$ echo --binary "This is a binary message"
01010100 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01100001 00100000 01100010 01101001 01101110 01100001 01110010 01111001 00100000 01101101 01100101 01110011 01110011 01100001 01100111 01100101

Or, more realistic:

$ echo "This is a binary message" | ascii2bin
01010100 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01100001 00100000 01100010 01101001 01101110 01100001 01110010 01111001 00100000 01101101 01100101 01110011 01110011 01100001 01100111 01100101

And also the reverse:

$ echo "01010100 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01100001 00100000 01100010 01101001 01101110 01100001 01110010 01111001 00100000 01101101 01100101 01110011 01110011 01100001 01100111 01100101" | bin2ascii
This is a binary message

PS: I'm using bash

PS2: I hope I didn't get the wrong binary

αғsнιη
  • 40,939
  • 15
  • 71
  • 114
RSFalcon7
  • 4,367
  • 6
  • 30
  • 56
  • Can you post what you're ultimately going to want to do with this? Just b/c whatever solutions we provide will likely only work for a narrow use case, and I get the sense you'll be asking for something that you'll want to use in a more complex way, and any solutions provided will likely fail in that scenario. – slm Nov 05 '13 at 15:33
  • @slm done, I edited the question – RSFalcon7 Nov 05 '13 at 15:39
  • I take it you want to ignore the LF character output by `echo`. – Stéphane Chazelas Nov 05 '13 at 15:57
  • The best way to understand this is that everything is binary. What you are trying to do is produce an ascii string of binary digits that represent the binary of the original ascii codded message. Therefore it is irrelevant that the original is ascii coded (well almost, as long as it is). Now you just need a tool that can print the binary as text. (there are already answer to tell you how). – ctrl-alt-delor Apr 01 '17 at 16:18

6 Answers6

53
$ echo AB | perl -lpe '$_=unpack"B*"'
0100000101000010
$ echo 0100000101000010 | perl -lpe '$_=pack"B*",$_'
AB
  • -e expression evaluate the given expression as perl code
  • -p: sed mode. The expression is evaluated for each line of input, with the content of the line stored in the $_ variable and printed after the evaluation of the expression.
  • -l: even more like sed: instead of the full line, only the content of the line (that is, without the line delimiter) is in $_ (and a newline is added back on output). So perl -lpe code works like sed code except that it's perl code as opposed to sed code.
  • unpack "B*" works on the $_ variable by default and extracts its content as a bit string walking from the highest bit of the first byte to the lowest bit of the last byte.
  • pack does the reverse of unpack. See perldoc -f pack for details.

With spaces:

$ echo AB | perl -lpe '$_=join " ", unpack"(B8)*"'
01000001 01000010
$ echo 01000001 01000010 | perl -lape '$_=pack"(B8)*",@F'
AB

(it assumes the input is in blocks of 8 bits (0-padded)).

With unpack "(B8)*", we extract 8 bits at a time, and we join the resulting strings with spaces with join " ".

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
37

You can use xxd to convert from ASCII and binary.

$ echo -n "A" | xxd -b
0000000: 01000001                                               A

$ echo -n "A" | xxd -b | awk '{print $2}'
01000001

Converting bases

If you're looking to do just base conversions between Hex, Octal, & Dec I usually use the basic calculator command line tool (bc) to do such things. Note that bc is always very picky about the correct order of bases: you have to specify the resulting base (obase) first, then add your choice of ibase.

$ echo "obase=2; ibase=16; A" | bc
1010

$ echo "obase=16; ibase=2; 1010" | bc
A
slm
  • 363,520
  • 117
  • 767
  • 871
  • `xxd` would be nice, except it shows that first annoying column and the base input at the end of the line. – RSFalcon7 Nov 05 '13 at 15:27
  • @RSFalcon7 - I know, you can pipe it to `awk` to get rid of it but it doesn't appear to have switches to disable this display. `| awk '{print $2}'`. There are other tools too. `od` & `hexdump`. I'm looking for another method using those. – slm Nov 05 '13 at 15:30
  • 1
    @RSFalcon7 use the `-p` option to get 'pure' output – AncientSwordRage Jan 22 '14 at 22:51
  • 1
    I use the `xxd -b` approach myself, however, one can't use `xxd` alone to convert binary back to ASCII. Todo so, I fear you would have to use something like `printf 'obase=16;ibase=2;%s\n' "$n" | bc | xxd -p -r` , with $n containing the number to convert, either as a long string of digits, or a strings of digits separated with semi-colons. If you can guarantee that $n fits into your shells's arithmetic type, then you may get away with `printf '%x\n' $((2#$n)) | xxd -p -r` – Franki Dec 02 '14 at 08:41
  • Unless you can use something like perl/python – Franki Dec 02 '14 at 08:46
  • @Pureferret unfortunately, `-p` doesn't work with binary output – Franki Dec 02 '14 at 08:48
  • can `xxd` output without addr offset? – Zimba Jan 29 '21 at 07:13
13

Using bc and bash:

#!/bin/bash

chrbin() {
        echo $(printf \\$(echo "ibase=2; obase=8; $1" | bc))
}

ordbin() {
  a=$(printf '%d' "'$1")
  b=$(echo "obase=2; $a" | bc)
  printf '%08d' $b 
}

ascii2bin() {
    echo -n $* | while IFS= read -r -n1 char
    do
        ordbin $char | tr -d '\n'
        echo -n " "
    done
}

bin2ascii() {
    for bin in $*
    do
        chrbin $bin | tr -d '\n'
    done
}
ascii2bin "This is a binary message"
bin2ascii 01010100 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01100001 00100000 01100010 01101001 01101110 01100001 01110010 01111001 00100000 01101101 01100101 01110011 01110011 01100001 01100111 01100101
Frederik Deweerdt
  • 3,722
  • 17
  • 18
6

Shell solution to convert binary to ascii:

bin2ascii() { { tr -cd 01 | fold -w8; echo; } | sed '1i obase=8; ibase=2' | bc | sed 's/^/\\/' | tr -d '\n' | xargs -0 echo -e; }
n.caillou
  • 393
  • 2
  • 7
  • 4
    This is not a pure shell solution. `sed`, `tr`, and `bc` are external programs called in the shell script. – Yokai Feb 24 '18 at 11:38
5

In Python

For ASCII characters in the range [ -~] on Python 2:

>>> import binascii
>>> bin(int(binascii.hexlify('hello'), 16))
'0b110100001100101011011000110110001101111'

In reverse:

>>> n = int('0b110100001100101011011000110110001101111', 2)
>>> binascii.unhexlify('%x' % n)
'hello'

In Python 3.2+:

>>> bin(int.from_bytes('hello'.encode(), 'big'))
'0b110100001100101011011000110110001101111'

In reverse:

>>> n = int('0b110100001100101011011000110110001101111', 2)
>>> n.to_bytes((n.bit_length() + 7) // 8, 'big').decode()
'hello'
serv-inc
  • 620
  • 5
  • 15
2

Using Python 3:

#!/usr/bin/env python3

import sys
from typing import Literal


def parse(from_format: Literal['bin', 'ascii'], data: str):
    if from_format == 'bin':
        binary = int(data.replace(' ', ''), 2)
        byte_length = (binary.bit_length() + 7) // 8
        return binary.to_bytes(byte_length, 'big').decode()
    if from_format == 'ascii':
        return (bin(int.from_bytes(data.encode(), 'big')))[2:]


def raise_usage():
    example_message = '''
        USAGE:
            decode -b 10000001
            decode -a "Hello, world!"'''
    raise ValueError(example_message)


if __name__ == "__main__":
    if len(sys.argv) <= 2:
        raise_usage()
    from_format, data = None, None
    if sys.argv[1] in ('-b', '--binary'):
        from_format = 'bin'
        data = ''.join(sys.argv[2:])
    if sys.argv[1] in ('-a', '--ascii'):
        from_format = 'ascii'
        data = ' '.join(sys.argv[2:])
    if not from_format or not data or from_format not in ('bin', 'ascii'):
        raise_usage()

    result = parse(from_format, data)
    print(result)

Saved as "bin2ascii" for example:

$ bin2ascii -a "Hello, world!"
1001000011001010110110001101100011011110010110000100000011101110110111101110010011011000110010000100001

$ bin2ascii -b 1001000011001010110110001101100011011110010110000100000011101110110111101110010011011000110010000100001  
Hello, world!
Vishtar
  • 21
  • 3