Sh, awk: как преобразовать целые числа, видимые в командной строке в виде строки, в 16-разрядные двоичные числа с младшим / большим порядком следования?

#linux #bash #shell #awk #sh

#linux #bash #оболочка #awk #sh

Вопрос:

У меня есть последовательность bash,

 grep "integer =" $1 | awk -F= '{printf("%dn",int($2*327))}'
  

которые фильтруют что-то, производящее что-то вроде:

 6768
6572
6638
8403
8436
8436
8305
8502
  

Однако мне нужно, чтобы все эти числа были помещены в двоичный файл в виде 16-разрядных слов младшего порядка (или с большим порядком, если указано). Есть ли какой-либо awk-, bash-способ сделать это?

В идеале это могло бы выглядеть как:

grep "integer =" $1 | awk -F='{TO16BIT_LENDIAN(printf("%dn",int($2*327)))}' >> out.bin

Комментарии:

1. 1. вы хотите, чтобы ваши выходные данные в .bin разделялись новыми строками? 2. Возможно, в Perl есть что-то, что может сделать это так, как вы себе это представляете (я буду удивлен, если это возможно с помощью предлагаемых вами инструментов). 3. Для чего-то такого простого вы могли бы написать код на языке C. (возможно, сделать проги 2 сентября для больших и маленьких конечных чисел). 4. удачи!

2. @shellter: 1.no, просто поток байтов 2. ну, может быть, python, но я бы хотел остаться с awk / bash, если это возможно 3.python -c «blabla», вероятно, лучше, потому что компиляции можно избежать 4. спасибо!

Ответ №1:

Это должно сработать:

 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%6;
  out(lsb);
  out(msb);
}

{
  shortToLE($2*327)
}
' >> out.bin
  

и оптимизированный способ удаления бесполезных cat и 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%6;
  out(lsb);
  out(msb);
}

$1 == "integer" {
  shortToLE($2*327)
}
' $1 >> out.bin
  

Комментарии:

1. Черт возьми! Ты опередил меня в этом. Однако вы можете избавиться от cat и просто поместить имя файла в конце awk команды. Вы также можете избавиться от grep, выполнив поиск «integer=» в awk, а затем используя «index» и «substr», чтобы удалить часть «integer=». В итоге вы просто остаетесь с одной awk-программой. Несмотря на «бесполезный cat», я все равно поставлю вам 1.

2. Спасибо, что указали на это. Я был слишком сосредоточен на несколько сложной части awk и упустил из виду то, что ее окружало, что я в основном просто вырезал и вставлял. Добавляю оптимизированный способ к моему ответу.

Ответ №2:

Запись в файл с определенным форматом обычно выполняется с помощью языка более высокого уровня. Пример с Ruby (где ваш входной файл находится в $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 Array#pack описан здесь.

Обновить:

используя -n переключатель:

 ruby -ne '
  BEGIN {fh = File.open("out.bin","wb")}
  fh.write( [(Float($_) * 327).to_i].pack("v") )
' numbers
  

Комментарии:

1. Вероятно, выглядело бы менее загроможденным использование ruby -ne

2. @glenn: есть ли причина записать это в файл, а не в стандартный вывод?

3. @ninjalj, это требование вопроса.

4. @glenn: Я не думаю, что обязательно делать все на одном языке, просто перенаправьте стандартный вывод в файл, для этого есть -n и -p , чтобы включить однострочники вместе с оболочкой.

Ответ №3:

А теперь о неприятной правде, что скрипт, использующий printf(«%c», данные), больше не работает

И вот мое чертовски уродливое, полностью измененное решение. Ого!

 # 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)));}
  

Здесь могут потребоваться некоторые пояснения.
Когда мы пишем наши скрипты, предполагается, что

 export LANG=C
  

установлено. Однако, когда при

 en_US.UTF-8
  

затем срабатывает корректность POSIX, и теперь вы больше не можете играть с байтами, как вы бы это делали, вместо этого вынужденно обрабатывая каждый символ как наименьшую единицу.
Что это означает

 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.
  

Теперь вы больше не можете печатать необработанные байты размером 128.

Это потому, что спецификация UTF-8 говорит нам об этом.
И вот важная часть
Большинство программ, которые преобразуют между кодами символов utf-8, выполняют для этого простую битовую операцию. Когда мы вводим значение 0xd800 или grater в эти празеры, чаще всего, хотя функция недокументирована, она позволяет печатать необработанные байты, как в старых системах

Это совершенно уродливый взлом, от которого вы не должны зависеть. Насколько я знаю, такой спецификации не существует или она не приходит мне в голову. Пожалуйста, скажите мне, есть ли одно такое.

Однако, когда вы работаете в системе, которая имеет неправильное значение LANG, или когда вашему скрипту необходимо обрабатывать символы utf-8 в большей части скрипта, за исключением только выходных данных, тогда это можно считать временным обходным путем, пока каким-то образом мы не сможем установить LANG или equlavant из скрипта.

Последняя проверка на gawk 4.0.1

Я ненавижу этот взлом