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).
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.
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
As a oneliner
echo -n "000000000000f03f" | while read -N2 code; do printf "\x$code"; done | od -t f8
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?
来源:https://stackoverflow.com/questions/36791622/how-to-print-float-value-from-binary-file-in-shell