web-dev-qa-db-fra.com

Conversion d'hex en décimal en awk ou sed

J'ai une liste de nombres séparés par des virgules:

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

Je dois diviser la 3ème colonne en trois comme ci-dessous:

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

Et convertissez les chiffres des deux dernières colonnes en décimal:

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

Voici une variation de la réponse de Jonathan:

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

J'ai inclus une méthode plutôt compliquée consistant à ajouter l'option --non-decimal-data si nécessaire.

Modifier

Juste pour le plaisir, voici l'équivalent de pure-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

Le "${line[*]:0:3}" (cité *) fonctionne de manière similaire à la variable OFS d'AWK en ce sens qu'il entraîne l'insertion de la variable IFS de Bash (ici une virgule) entre les éléments du tableau en sortie. Nous pouvons tirer davantage parti de cette fonctionnalité en insérant les éléments du tableau comme suit, qui se rapprochent davantage de la version AWK ci-dessus.

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

Malheureusement, Bash n'autorise pas printf -v (similaire à sprintf()) à assigner des éléments de tableau, donc printf -v "line[6]" ... ne fonctionne pas.

Éditer: À partir de Bash 4.1, printf -v peut maintenant assigner des éléments de tableau. Exemple:

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

Les guillemets autour de la référence du tableau sont nécessaires pour empêcher une éventuelle correspondance du nom de fichier. Si un fichier nommé "line6" existait dans le répertoire actuel et que la référence n'était pas citée, une variable nommée line6 serait créée (ou mise à jour) contenant la sortie printf. Rien d’autre dans le fichier, tel que son contenu, n’entre en jeu. Seul le nom - et seulement de manière tangentielle.

22
Dennis Williamson

Cela semble fonctionner:

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

Pour vos exemples de données d'entrée, il produit:

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

La concaténation de chaîne de '0x' plus l'hex à 4 chiffres suivi de l'ajout de 0 force awk pour traiter les nombres en tant qu'hexadécimaux.

Vous pouvez simplifier ceci pour:

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

Les chaînes avec le préfixe 0x sont forcées en entier lorsqu'elles sont présentées au format printf() et au format %d.


Le code ci-dessus fonctionne à merveille avec la awk native sur MacOS X 10.6.5 (version 20070501); Malheureusement, cela ne fonctionne pas avec GNU gawk 3.1.7. Cela, semble-t-il, est un comportement autorisé selon POSIX (voir les commentaires ci-dessous). Cependant, gawk a une fonction non standard strtonum qui peut être utilisée pour le matraquer correctement, dommage qu'il soit nécessaire de le matraquer.

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);
          }'
9
Jonathan Leffler

Par AWK

Cette réponse vise à montrer comment effectuer la conversion de façon portative avec awk.

L'utilisation de --non-decimal-data pour gawk n'est pas recommandée selon GNU Guide de l'utilisateur Awk . Et utiliser strtonum() n'est pas portable.

Dans les exemples suivants, le premier mot de chaque enregistrement est converti.

Par fonction définie par l'utilisateur

La méthode de conversion la plus portable consiste à utiliser une fonction awk définie par l'utilisateur [ référence ]:

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

En appelant le printf de Shell

Vous pourriez utiliser ceci

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

mais c'est relativement lent. La suivante est plus rapide si vous devez convertir plusieurs nombres hexadécimaux séparés par une ligne-nouvelle:

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

Il peut y avoir un problème si de très nombreux arguments sont ajoutés pour la seule commande printf.

Sous Linux

D'après mon expérience, les travaux suivants sous Linux:

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

Je l'ai testé avec gawk, mawk et original-awk dans Ubuntu Linux 14.04. Par original-awk, la commande affiche un message d’avertissement, mais vous pouvez le masquer à l’aide de la directive de redirection 2>/dev/null dans Shell. Si vous ne voulez pas faire cela, vous pouvez effacer le -Wposix en cas de original-awk comme ceci:

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

(Dans Bash 4, vous pouvez remplacer >/dev/null 2>&1 par &>/dev/null)

Remarque: L'astuce -Wposix ne fonctionne probablement pas avec nawk, qui est utilisé dans OS X et certaines variantes du système d'exploitation BSD.

5
jarno

Cela pourrait fonctionner pour vous (GNU sed & printf):

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

Fractionnez les huit derniers caractères et ajoutez les espaces précédant les champs avec l'identificateur hexadécimal, puis évaluez la ligne entière à l'aide de printf.

0
potong
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) }'

La commande ci-dessus imprime le contenu de "all_info_List.csv", un fichier dans lequel le séparateur de champ est "|". Prend ensuite le champ 21 (adresse MAC) et le divise à l'aide du séparateur de champ ":". Il attribue à la variable "p1" les 5 premiers octets de chaque adresse MAC. Si nous avions cette adresse: "11: 22: 33: 44: 55: 66", p1 serait: "11:22:33 : 44: 55: ". p2 se voit attribuer la valeur décimale du dernier octet:" 0x66 "assignerait" 102 "décimal à p2. Enfin, j'utilise printf pour rejoindre p1 et p2, lors de la conversion de p2 au format hexadécimal, après en avoir ajouté un.

0
Sophoclis
printf "%d\n", strtonum( "0x"$1 )"
0
user164485