10

Let's say there is a variable in a Bash script with the value "001". How can I write this binary data into a file as bits (as "001" not "1")

echo writes it as a string but I want to write in bits.

4
  • 2
    You can't write individual bits to a file. The smallest amount you can write is 8 bits, i.e. a byte. Commented Apr 4, 2017 at 17:41
  • 2
    how do i write 8 bit string variable say "00000011" to file Commented Apr 4, 2017 at 17:42
  • 1
    @JeevansaiJinne printf '%s' $'\x03' > file Commented Apr 4, 2017 at 17:48
  • 3
    @123 This works, but only as long as the value is not \x00, as this value can not be represented as an argument. printf '\x00' works though, since it avoids passing binary data literally Commented Apr 4, 2017 at 17:58

6 Answers 6

19

You can write arbitrary bytes in hex or octal with:

printf '\x03' > file   # Hex
printf '\003' > file   # Octal

If you have binary, it's a bit tricker, but you can turn it into octal with:

printf '%o\n' "$((2#00000011))"

which of course can be nested in the above:

binary=00000011
printf "\\$(printf '%o' "$((2#$binary))")" > file

Note that this only works with up to 8 bits. If you want to write longer values, you have to split it up into groups of 8.

Sign up to request clarification or add additional context in comments.

2 Comments

what is the difference between writing in decimal and octal both are writing bytes
The result is the same, the difference is only in how you specify the byte you want to write. If you want to write all ones, it's \xFF vs \377. You can use whichever you prefer to work with.
3

Just use the %b in the printf:

printf "%b" "\012"
printf "%b" "\x0a"

%b - Print the associated argument while interpreting backslash escapes in there

so, above both prints binary value for the decimal 10.

printf "%b" "\x0a" | od -bc

output

0000000   012                                                            
          \n      

you can even mix

printf "%b" "\12\xa\n" | od -bc

0000000   012 012 012                                                    
          \n  \n  \n            

1 Comment

I'd suggest single quotes rather than double quotes, just to make it absolutely clear that the exact characters you enter are being passed to printf without anything happening on the way.
3

A couple of more general functions to output integers to a file:

le16 () { # little endian 16 bit;  1st param: integer to 2nd param: file 
  v=$(printf "%04X" "$1")
  printf "%b" "\\x${v:2:2}\\x${v:0:2}" >> $2
}

le32 () { # 32 bit version
  v=$(printf "%08X" "$1")
  printf "%b" "\\x${v:6:2}\\x${v:4:2}\\x${v:2:2}\\x${v:0:2}" >> $2
}

3 Comments

Is awk really helpful here? Using plain printf seems much cleaner to me: v=$(printf "%04X" "$1")
Boo, hiss re: echo -e (which has behavior explicitly undefined by POSIX, and which in bash may or may not work depending on the xpg_echo and posix configuration flags). The answers suggesting printf are much more reliable. See pubs.opengroup.org/onlinepubs/9799919799/utilities/…, specifically including the APPLICATION USAGE and RATIONALE sections.
Thanks! See version with printfs -- but there should be a simpler way.
1

Using bc (in CentOS 7.4):

VALUE="3F"
INPUT_BASE=16
OUTPUT_BASE=2
printf "%b\n" $(bc <<< "ibase=$INPUT_BASE; obase=$OUTPUT_BASE; $VALUE")

RESULT: 111111

If leading zeros are needed then:

VALUE="3F"
INPUT_BASE=16
OUTPUT_BASE=2
printf "%16b\n" $(bc <<< "ibase=$INPUT_BASE; obase=$OUTPUT_BASE; $VALUE") | sed 's^ ^0^g'

RESULT: 0000000000111111

Note the use of 16 in the printf format; change value to limit leading-zero count

Comments

1

Most often you want to write a pattern (for instance a 32 bit pattern).

# Write 32 bits 5 times to a file 
for i in {1..5}
do
   printf '\x12\x34\xAC\xCD' >> binaryfile.bin
done

Another example:

for i in {1..255}
do
    hx=$( printf "%x" $i )
    output="\x$hx\x00\x00\x00"
    printf "%b" $output >> binaryfile.bin 
done

Comments

1

I needed this recently, someone might find it useful :

#!/bin/bash

# generate bitmasks for a C array

# loop counter
NUMBER=0
MAX_BITMASK_LENGTH=8
# max loop value : 2^8 = 255
MAXVAL=$((2**MAX_BITMASK_LENGTH))
# line number
LINE=0

# echo start of C array
echo "    const long int bitmasks[${MAX_BITMASK_LENGTH}] = {"

# loop over range of values
while [ ${NUMBER} -le ${MAXVAL} ] ; do

    # use bc to convert to binary
    BINARY=`echo -e "obase=2\n${NUMBER}"| bc`

    # print current number, linenumber, binary
    printf "%12s, /* (%i) %8s */\n" ${NUMBER} ${LINE} ${BINARY}

    # increase loop value to next value
    NUMBER=$(((NUMBER*2)+1))

    # increment line number
    LINE=$((LINE+1))
done

# echo end of C array
echo "        };"

This outputs:

$ ./generate_bitmasks.sh
    const long int bitmasks[8] = {
           0, /* (0)        0 */
           1, /* (1)        1 */
           3, /* (2)       11 */
           7, /* (3)      111 */
          15, /* (4)     1111 */
          31, /* (5)    11111 */
          63, /* (6)   111111 */
         127, /* (7)  1111111 */
         255, /* (8) 11111111 */
        };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.