Search code examples
shellcommand-linefloating-pointbinary-dataxxd

How to print float value from binary file in shell?


I've binary file consisting double float (8 bytes) or just float (4 bytes) value which was generated in the following way:

$ python -c $'from struct import pack\nwith open("file.bin", "wb") as f: f.write(pack("<d", 0.123))'
$ xxd file.bin
00000000: b072 6891 ed7c bf3f                      .rh..|.?

which I could print it back from Python via:

$ python -c $'from struct import unpack\nwith open("file.bin", "rb") as f: print(unpack("<d", f.read(8)))'
(0.123,)

The same for 4-byte float, just change <d into <f (mentioned as float.bin later on).

How do I print that value from the shell script using cleaner way (without using Python)? Either using built-in tools (e.g. printf), or wide-used external tools (e.g. xxd, dc, bc, od, hexdump, etc.).


For example to print decimal values, I can use xxd (part of Vim), e.g. in Bash:

  • get the value of first byte:

    $ echo $((16#$(xxd -ps -s0 -l1 file.bin)))
    176
    

    For 2nd and forth bytes, increase -s.

  • get decimal value from all 8 bytes:

    $ echo $((16#$(xxd -ps -s0 -l8 file.bin)))
    -5732404399725297857
    

However I would like to print original floating value (0.123) on Unix-family system. Ideally using some one-liner (to keep it simple as part of the script), so I can assign it into text variable or print the value on the screen.


I've tried to use printf (on the 4-byte float number to make it simpler), but it didn't work as expected:

$ xxd -p float.bin
6de7fb3d
$ printf "%.4f\n" 0x.$(xxd -p float.bin)
0.4293
$ printf "%.4f\n" 0x3dfbe76d
1039918957.0000
$ printf "%.4e\n" 0x3dfbe76d
1.0399e+09

where according to this on-line converter, 0x3dfbe76d is the same as 0.123, so the hex values are in reverse (what xxd actually gives).


Solution

  • This doesn't use Python and is a widely-used external tool, Perl.

    perl -e "print pack('d>',0.123)" > file.bin
    
    perl -e "print unpack('d>',<>)" < file.bin
    0.123
    

    Or you can use GNU od utility, e.g.:

    od -tfD file.bin
    0000000                    0.123
    0000010
    

    Where -t parameter specifies the output format for floating-point number (f) followed by optional size specifier (F for float, D for double or L for long double), in short -tfD can be replaced by -e or -F. To print only value without address, -A n can be specified.