Преобразование шестнадцатеричного числа в десятичное в awk или sed - PullRequest
18 голосов
/ 06 января 2011

У меня есть список чисел, разделенных запятыми:

123711184642,02,3583090366663629,639f02012437d4
123715942138,01,3538710295145500,639f02afd6c643
123711616258,02,3548370476972758,639f0200485732

Мне нужно разделить 3-й столбец на три, как показано ниже:

123711184642,02,3583090366663629,639f02,0124,37d4
123715942138,01,3538710295145500,639f02,afd6,c643
123711616258,02,3548370476972758,639f02,0048,5732

И преобразовать цифры впоследние два столбца в десятичном виде:

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322

Ответы [ 7 ]

22 голосов
/ 06 января 2011

Вот вариант ответа Джонатана:

awk $([[ $(awk --version) = GNU* ]] && echo --non-decimal-data) -F, '
    BEGIN {OFS = FS}
    {
        $6 = sprintf("%d", "0x" substr($4, 11, 4))
        $5 = sprintf("%d", "0x" substr($4,  7, 4))
        $4 = substr($4,  1, 6)
        print
    }'

Я включил довольно искаженный способ добавления опции - non-decimal-data , если это необходимо.

Редактировать

Просто, черт возьми, вот эквивалент чистого Bash:

saveIFS=$IFS
IFS=,
while read -r -a line
do
    printf '%s,%s,%d,%d\n' "${line[*]:0:3}" "${line[3]:0:6}" "0x${line[3]:6:4}" "0x${line[3]:10:4}"
done
IFS=$saveIFS

"${line[*]:0:3}" (в кавычках *) работает аналогично OFS AWK в том смысле, что вызывает вставку IFS (здесь запятая) Bash между элементами массива на выходе. Мы можем воспользоваться этим преимуществом, вставив элементы массива следующим образом, что более точно соответствует моей версии AWK, приведенной выше.

saveIFS=$IFS
IFS=,
while read -r -a line
do
    line[6]=$(printf '%d' "0x${line[3]:10:4}")
    line[5]=$(printf '%d' "0x${line[3]:6:4}")
    line[4]=$(printf '%s' "${line[3]:0:6}")
    printf '%s\n' "${line[*]}"
done
IFS=$saveIFS

К сожалению, Bash не позволяет printf -v (что похоже на sprintf()) назначать элементы массива, поэтому printf -v "line[6]" ... не работает.

Редактировать: Начиная с Bash 4.1, printf -v теперь может назначать элементы массива. Пример:

printf -v 'line[6]' '%d' "0x${line[3]:10:4}"

Кавычки вокруг ссылки на массив необходимы для предотвращения возможного совпадения имени файла. Если в текущем каталоге существует файл с именем "line6", а ссылка не указана в кавычках, то будет создана (или обновлена) переменная с именем line6, содержащая выходные данные printf. Ничто другое о файле, например, его содержимое, не вступит в игру. Только имя - и только косвенно.

9 голосов
/ 06 января 2011

Кажется, это работает:

awk -F, '{ p1 =       substr($4,  1, 6);
           p2 = ("0x" substr($4,  7, 4)) + 0;
           p3 = ("0x" substr($4, 11, 4)) + 0;
           printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, p2, p3;
         }'

Для ваших входных данных сэмпла выдает:

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322

Конкатенация строк «0x» плюс 4-значный шестнадцатеричный код, за которым следуетдобавив 0 сил awk для обработки чисел как шестнадцатеричных.

Вы можете упростить это до:

awk -F, '{ p1 =      substr($4,  1, 6);
           p2 = "0x" substr($4,  7, 4);
           p3 = "0x" substr($4, 11, 4);
           printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, p2, p3;
         }'

Строки с префиксом 0x становятся целочисленными при представлении printf() иформат %d.


Приведенный выше код прекрасно работает с собственным awk на MacOS X 10.6.5 (версия 20070501);к сожалению, он не работает с GNU gawk 3.1.7.Это, похоже, разрешенное поведение в соответствии с POSIX (см. Комментарии ниже).Тем не менее, gawk имеет нестандартную функцию strtonum, которую можно использовать, чтобы заставить его работать правильно - жалко, что необходимо забивать.

gawk -F, '{ p1 =      substr($4,  1, 6);
            p2 = "0x" substr($4,  7, 4);
            p3 = "0x" substr($4, 11, 4);
            printf "%s,%s,%s,%s,%d,%d\n", $1, $2, $3, p1, strtonum(p2), strtonum(p3);
          }'
6 голосов
/ 07 сентября 2015

по AWK

Этот ответ концентрируется на демонстрации того, как переносить awk переносимо.

Использование --non-decimal-data для gawk не рекомендуется согласно Руководство пользователя GNU Awk .И использование strtonum() не переносимо.

В следующих примерах первое слово каждой записи преобразуется.

По пользовательской функции

Самый переносимый способвыполнение преобразования осуществляется с помощью пользовательской функции awk [ reference ]:

function parsehex(V,OUT)
{
    if(V ~ /^0x/)  V=substr(V,3);

    for(N=1; N<=length(V); N++)
        OUT=(OUT*16) + H[substr(V, N, 1)]

    return(OUT)
}

BEGIN { for(N=0; N<16; N++)
        {  H[sprintf("%x",N)]=N; H[sprintf("%X",N)]=N } }

{ print parsehex($1) }

Вызывая команду printf

Вы можете использовать это

awk '{cmd="printf %d 0x" $1; cmd | getline decimal; close(cmd); print decimal}'

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

awk 'BEGIN{cmd="printf \"%d\n\""}{cmd=cmd " 0x" $1}END{while ((cmd | getline dec) > 0) { print dec }; close(cmd)}'

Может возникнуть проблема, если для одной команды printf добавлено очень много аргументов.

В Linux

По моему опыту в Linux работает следующее:

awk -Wposix '{printf("%d\n","0x" $1)}'

Я тестировал его с помощью gawk, mawk и original-awk в Ubuntu Linux 14.04.При использовании original-awk команда отображает предупреждающее сообщение, но вы можете скрыть его с помощью директивы перенаправления 2>/dev/null в оболочке.Если вы не хотите этого делать, вы можете удалить -Wposix в случае оригинального awk следующим образом:

awk $(awk -Wversion >/dev/null 2>&1 && printf -- "-Wposix") '{printf("%d\n","0x" $1)}'

(В Bash 4 вы можете заменить >/dev/null 2>&1 на &>/dev/null)

Примечание: трюк -Wposix, вероятно, не работает с nawk, который используется в OS X и некоторых вариантах ОС BSD.

0 голосов
/ 19 июля 2018
printf "%d\n", strtonum( "0x"$1 )"
0 голосов
/ 04 ноября 2015

версия Perl, с подсказкой @Jonathan:

perl -F, -lane '$p1 = substr($F[3], 0, 6); $p2 = substr($F[3], 6, 4); $p3 = substr($F[3], 10, 4); printf "%s,%s,%s,%s,%d,%d\n", @F[0..2], $p1, hex($p2), hex($p3)' file

-a включить режим автоматического разделения, чтобы заполнить массив @F
-F, изменяет разделитель автоматического разделения на , (по умолчанию используется пробел)
Индексы substr() на 1 меньше их эквивалентов awk, поскольку массивы Perl начинаются с 0.

Выход:

123711184642,02,3583090366663629,639f02,292,14292
123715942138,01,3538710295145500,639f02,45014,50755
123711616258,02,3548370476972758,639f02,72,22322
0 голосов
/ 08 сентября 2015

Это может работать для вас (GNU sed & printf):

sed -r 's/(....)(....)$/ 0x\1 0x\2/;s/.*/printf "%s,%d,%d" &/e' file

Разделите последние восемь символов и добавьте пробелы, предшествующие полям с помощью шестнадцатеричного идентификатора, а затем оцените всю строку, используя printf.

0 голосов
/ 05 марта 2014
cat all_info_List.csv| awk 'BEGIN {FS="|"}{print $21}'| awk 'BEGIN {FS=":"}{p1=$1":"$2":"$3":"$4":"$5":";  p2 = strtonum("0x"$6); printf("%s%02X\n",p1,p2+1) }'

Приведенная выше команда печатает содержимое файла "all_info_List.csv", в котором разделителем полей является "|".Затем берет поле 21 (MAC-адрес) и разделяет его, используя разделитель полей «:».Он назначает переменной "p1" первые 5 байтов каждого mac-адреса, поэтому, если бы у нас был этот mac-адрес: "11: 22: 33: 44: 55: 66", p1 было бы: "11:22: 33: 44: 55:».p2 назначается с десятичным значением последнего байта: "0x66" назначит десятичному значению "102" значение p2.Наконец, я использую printf для объединения p1 и p2, при конвертации p2 обратно в гекс после добавления к нему одного.

...