There is a similar tool called xxd. If you run xxd with just a file name it dumps the data in a fairly standard hex dump format:
# xxd bdata
0000000: 0001 0203 0405
......
Now if you pipe the output back to xxd with the -r option and redirect that to a new file, you can convert the hex dump back to binary:
# xxd bdata | xxd -r >bdata2
# cmp bdata bdata2
# xxd bdata2
0000000: 0001 0203 0405
Answer from Bert on Stack OverflowThere is a similar tool called xxd. If you run xxd with just a file name it dumps the data in a fairly standard hex dump format:
# xxd bdata
0000000: 0001 0203 0405
......
Now if you pipe the output back to xxd with the -r option and redirect that to a new file, you can convert the hex dump back to binary:
# xxd bdata | xxd -r >bdata2
# cmp bdata bdata2
# xxd bdata2
0000000: 0001 0203 0405
Restore file, given only the output of hexdump file
If you only have the output of hexdump file and want to restore the original file, first note that hexdump's default output depends on the endianness of the system you ran hexdump on!
If you have access to the system that created the dump, you can determinate its endianness using below command:
[[ "$(printf '\01\03' | hexdump)" == *0103* ]] && echo big || echo little
Reversing little-endian hexdump
This is the most common case. All x86/x64 systems are little-endian. If you don't know the endianness of the system that ran hexdump file, try this.
sed 's/ \(..\)\(..\)/ \2\1/g;$d' dump | xxd -r
The sed part converts hexdump's format into xxd's format, at least so far that xxd -r works.
Reversing big-endian hexdump
sed '$d' dump | xxd -r
Known Bugs (see comment section)
- A trailing null byte is added if the original file was of odd length (e.g. 1, 3, 5, 7, ..., byte long).
- Repeating sections of the original file are not restored correctly if they were
hexdumped using a*.
You can check your dump for above problematic cases by running below command:
grep -qE '^\*|^[0-9a-f]*[13579bdf] *$' dump && echo bug || echo ok
Better alternative to create hexdumps in the first place
Besides the non-posix (and therefore not so portable) xxd there is od (octal dump) which should be available on all unix-like systems as it is specified by posix:
od -tx1 -An -v
Will print a hexadecimal dump, grouping digits as single bytes (-tx1), with no Address prefixes (-An, similar to xxd -p) and without abbreviating repeated sections as * (-v). You can reverse such a dump using xxd -r -p.
You will have to write your own binary to hex function ...
cut -f2-8 -d' ' infile | tr -d '\n' | tr ' ' '\n' | while read l;
do
echo -n $(bin_to_hex($l)) >> outfile
(( ++n ))
(( n % 60 == 0)) && echo "" >> outfile
done
This should output the same format as -p which can then be run through -r.
If you read the man page for xxd you will read that there is no -r for -b. It says so on the excerpt that you included in your question.
You alter the structure of the file do you can't easy do this. Maybe something like this can help (but you will loose newline characters:
awk '{printf $8}' binary_format.txt >out_file
Perl, 122 + 54 = 176 122 + 45 = 167
The forward script:
$/=$,;for(<>=~/.{1,16}/gs){$h="";$h.=sprintf"%*s%02x",++$m%2,"",ord for/./gs;
s/[^ -~]/./g;printf"%06x0:%-42s",$n++,$h;say}
And the reverse script:
/:(.+?) /,print map{chr hex}$1=~/\w\w/gfor<>
(This one is interesting; there are all kinds of obscure bugs that can show up in the reverse script depending on the input, if you're not careful.)
Javascript, 247 + 61 = 308 225 + 63 = 288
-20 or so thanks to ASCII-only
Forward:
b=>[...Array(b.length+15>>4)].map((_,i)=>(P=j=>i=>i.toString(x=16).padStart(j,0))(7)(i)+"0: "+(p=b.splice(0,x)).map(P(2)).join``.replace(/.{4}/g,"$& ").padEnd(42)+p.map(l=>String.fromCharCode(l>31&l<127?l:46)).join``).join`
`
Try it online!
Reverse:
d=>d.match(/[^:]{40}/g).join``.match(/\S\S/g).map(t=>+("0x"+t))
Try it online!
Might make an explanation at some point.
With fold + tac + tr :
$ echo 030201|fold -w2|tac|tr -d "\n"
010203
fold- split every 2 bytetac- reverse cattr- remove newlines
If your system has a rev command.
hex=030201
new_hex=$(printf %s "$hex" | dd conv=swab 2> /dev/null | rev)
If it has a tac or tail -r command:
new_hex=$(echo "$hex" | fold -w 2 | tac | paste -sd '\0' -)
With zsh:
setopt extendedglob
new_hex=${(j[])${(s[]Oa)${hex//(#b)(?)(?)/$match[2]$match[1]}}}
(like in the dd approach: swap pairs of characters, split into list of individual characters (s[]), reverse the order (Oa) and join (j[])).
Or:
printf -v new_hex '%2$s%1$s' ${(s[]Oa)hex}
POSIXly:
new_hex=$(
awk '
BEGIN {
hex = ARGV[1]; l = length(hex)
for (i = 1; i < l; i += 2)
new_hex = substr(hex, i, 2) new_hex
print new_hex
}' "$hex"
)
Or
new_hex=$(echo "$hex" |
sed -e 'G;:1' -e 's/\(..\)\(.*\n\)/\2\1/;t1' -e 's/.//')
With perl:
new_hex=$(perl -le 'print reverse(shift =~ /../g)' -- "$hex")