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
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.
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);
}'
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.
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) }
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.
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.
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.
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.
printf "%d\n", strtonum( "0x"$1 )"