Преобразование hex в десятичный в awk или sed

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

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

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

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

Ответы

Ответ 1

Здесь вариант ответа Джонатана:

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
    }'

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

Edit

Просто для этого, здесь чистый 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}" (цитируется *) работает аналогично AWK OFS в том смысле, что он вызывает Bash IFS (здесь запятую), которая должна быть вставлена ​​между элементами массива на выходе. Мы можем воспользоваться дополнительным преимуществом этой функции, вставив элементы массива следующим образом, которые более тесно параллельны моей версии 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 теперь может выполнять присвоения элементам массива.

Ответ 2

Это работает:

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" плюс четырехзначный шестнадцатеричный код с последующим добавлением 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);
          }'

Ответ 3

AWK

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

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

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

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

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

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.

Ответ 4

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 обратно в hex, после добавления к нему.

Ответ 5

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

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

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

Ответ 6

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, изменяет разделитель autosplit на , (по умолчанию пробел)
Индексы substr() на 1 меньше, чем их эквиваленты awk, поскольку массивы Perl начинаются с 0.

Вывод:

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