Change only one bit in a file

I don't think there's a single command. Here's a simple script, save it as "flipbit":

#!/usr/bin/perl
# Arguments:   byte (starting from 0),  bit (0-7),  filename (otherwise stdin)
$byte = shift(@ARGV);
$bit = shift(@ARGV);
undef $/; 
$file=<>; 
substr($file,$byte,1) = substr($file,$byte,1) ^ chr(1<<$bit); 
print $file;

test:

$ echo abb | ~/bin/flip-bit.pl 2 0 | od -xa
0000000      6261    0a63                                                
       a   b   c  nl                                                

this flipped the low-order bit (0) of the third character, changing the 'b' to 'c'.

As a single line command:

perl -e '$byte=shift(@ARGV);$bit=shift(@ARGV);undef $/; $file=<>; substr($file,$byte,1) = substr($file,$byte,1) ^ chr(1<<$bit); print $file'

Since the file may contain nulls, text-oriented filters like sed are going to fail. But you can use a programming language that can handle nulls, like perl or python. Here's a solution for Python 3. It's a few lines longer than strictly necessary, for readability.

#!/usr/bin/env python3
"""Toggle the bit at the specified offset.
Syntax: <cmdname> filename bit-offset"""

import sys
fname = sys.argv[1]
# Convert bit offset to bytes + leftover bits
bitpos = int(sys.argv[2])
nbytes, nbits = divmod(bitpos, 8)

# Open in read+write, binary mode; read 1 byte 
fp = open(fname, "r+b")
fp.seek(nbytes, 0)
c = fp.read(1)

# Toggle bit at byte position `nbits`
toggled = bytes( [ ord(c)^(1<<nbits) ] ) 
# print(toggled) # diagnostic output

# Back up one byte, write out the modified byte
fp.seek(-1, 1)  # or absolute: fp.seek(nbytes, 0)
fp.write(toggled)
fp.close()

Save it in a file (e.g., bitflip), make it executable, and run it with the filename to modify and the offset in bits. Note that it modifies the file in place. Run it twice with the same offset and you'll get your file restored.


Finally I found a solution with xxd and dd.

a=$(xxd -b -l 1 -seek 3 -p a.bin);b=1;echo -e "\x$((${a}^${b}))" | dd of=a.bin bs=1 seek=3 count=1 conv=notrunc

hexdump a.bin     v
0000000 61 39 73 36 36 64 66 38 61 39 73 64 35 36 66 35
0000010 37 61 73 64 37 66 74 75 61 67 73 0a 61 73 64 66

hexdump b.bin     v
0000000 61 39 73 37 36 64 66 38 61 39 73 64 35 36 66 35
0000010 37 61 73 64 37 66 74 75 61 67 73 0a 61 73 64 66

But this is ugly.

Tags:

Dd

Hashsum