How to print float value from binary file in shell

2019-04-08 15:39发布

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).

4条回答
We Are One
2楼-- · 2019-04-08 15:58

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.

查看更多
Bombasti
3楼-- · 2019-04-08 16:00

You may wish to consider some of the mentioned tools such as bc which can be used in arithmetic operations and comparisons - eg:

#!/bin/bash
A=1.3141592653581 # variable 1
B=1.3141592653589 # variable 2
if (( `echo $A"<"$B | bc` )) ; then printf "$A is: < $B\n" ; else printf "$B is: < $A\n" ; fi ;
C=$(echo "$A+$B" | bc)
printf "C == $C\n" ;

Additional formatting for related numbers and types such as zero may be required; as addressed in some other postings eg: How to show zero before decimal point in bc?

查看更多
相关推荐>>
4楼-- · 2019-04-08 16:09
od -f <filename>

That will dump your file as floats.

od is a standard Linux tool, and it's what I use. The manpage reads:

od - dump files in octal and other formats

查看更多
一夜七次
5楼-- · 2019-04-08 16:10

As a oneliner

echo -n "000000000000f03f" | while read -N2 code; do printf "\x$code"; done | od -t f8
查看更多
登录 后发表回答