Sh, awk: how to convert integers, visible in the shell as a string to 16-bit low-/big- endian binary integers?
I have a bash sequence,
grep "integer =" $1 | awk -F= '{printf("%d\n",int($2*327))}'
which filters something producing smth like:
6768
6572
6638
8403
8436
8436
8305
8502
I need however all these numbers to be placed in a binary file as 16-bit low-endian words (or big-endian if specified). Is there any awk-, bash- way to do it?开发者_JAVA百科
Ideally, it could look like:
grep "integer =" $1 | awk -F='{TO16BIT_LENDIAN(printf("%d\n",int($2*327)))}' >> out.bin
This should work:
cat $1 | grep "integer =" | awk -F='
function out(b)
{
if(b==0)
{
system("printf \"\\00\"");
}
else
{
printf("%c",b);
}
}
function shortToLE(n)
{
n%=65536;
msb=n/256;
lsb=n%256;
out(lsb);
out(msb);
}
{
shortToLE($2*327)
}
' >> out.bin
and the optimized way removing the useless cat and grep:
awk -F" =" '
function out(b)
{
if(b==0)
{
system("printf \"\\00\"");
}
else
{
printf("%c",b);
}
}
function shortToLE(n)
{
n%=65536;
msb=n/256;
lsb=n%256;
out(lsb);
out(msb);
}
$1 == "integer" {
shortToLE($2*327)
}
' $1 >> out.bin
Writing to a file with a specific format is usually done with a higher-level language. An example with Ruby (where your input file is in $1
:
ruby -e '
nums = File.readlines(ARGV[0]).collect {|line| (Float(line) * 327).to_i}
File.open("out.bin", "wb") do |fh|
fh.write( nums.pack("v*") )
end
' "$1"
Ruby's Array#pack method is documented here.
Update:
using -n
switch:
ruby -ne '
BEGIN {fh = File.open("out.bin","wb")}
fh.write( [(Float($_) * 327).to_i].pack("v") )
' numbers
And now for the ugly truth that the script using printf("%c",data) no longer works
And here is my ugly as hell totally * * * *ed up workaround. Augh!
# This ugly hack forces our broken system to pretend it works
MAGIC_SHIT=((ENVIRON[LANG]=="C")?0:0xd800);
function TO16BIT_LENDIAN(n){return sprintf("%c%c",(MAGIC_SHIT+and(n,0xff)),(MAGIC_SHIT+rshift(and(n,0xff00),8)));}
Some explanations migh be required here. When we write our scripts, it is assumed that
export LANG=C
is set. However, when when one has
en_US.UTF-8
then POSIX-correctness kicks in, and you are now no longer able to play with bytes as you would, instead forced with treating each character as the smallest unit. What this means is
0x00 up to 0x7f = 0xYY // sprintf("%c",n) prints ok
0x80 up to 0xbf = 0xc2 0xYY // sprintf("%c",n) prints 0xc2 in front
0xc0 up to 0xff = 0xc3 + 0x80..0xb0 // Totally junk, not what we want.
You are now no longer able to print raw bytes grater thatn 128.
Now, this is because the UTF-8 specifcation tells us to do so.
And here is the important part
Most prasers that convert between utf-8 character code, perform simple bit operation
to do so.
When we feed 0xd800 or grater value to these prasers, most often, while undocumented feature, allow you to print raw bytes as you would have on older systems
This is totally a ugly hack that you should not depend on. As far as I know, no such specification exsists, or comes off top of my head. Please tell me if there is one such.
However, when you are on a system that has the wrong LANG value, or when your script needs to handle utf-8 characters, on most part of the script, except just the data output, then it could be considered a temporary workaround, untill somehow we are able to set LANG or equlavant from within the script.
Last checked on gawk 4.0.1
I hate this hack
精彩评论