Преобразование 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