web-dev-qa-db-fra.com

Déplacer le motif au début de la ligne

J'essaie de reformater un fichier journal tel que la date et l'heure apparaissent au début de la ligne. Mes journaux ressemblent à ceci:

blah, blah, blah, Friday, Mar 13,2015 16:59:42
yadi, yadi, yada, Friday, Mar 13,2015 16:51:11

Je voudrais qu'ils ressemblent à ceci:

Friday, Mar 13,2015 16:59:42 blah, blah, blah
Friday, Mar 13,2015 16:51:11 yadi, yadi, yada

Je suis allé jusqu'à trouver le bon motif de grep avec grep -o -i -e '[a-zA-Z]*, [a-z][a-z][a-z] [0-9]*,[0-9][0-9][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]' ~/log.txt.

Comment puis-je déplacer ces résultats de modèle vers la gauche de la chaîne d'information? Merci de votre aide.

5
Tfb9

Essayez sed avec la regex suivante:

$ sed -i.bak 's_\(.*\),[[:blank:]]\([[:alpha:]]\+,[[:blank:]][[:alpha:]]\+[[:blank:]][[:digit:]]\+,[^,]\+$\)_\2 \1_' file.txt 
Friday, Mar 13,2015 16:59:42 blah, blah, blah
Friday, Mar 13,2015 16:51:11 yadi, yadi, yada

Ici, nous avons utilisé la méthode de substitution de groupe de la variable sed pour obtenir le résultat souhaité.

  • \(.*\) correspondra jusqu'à blah, blah, blah car nous avons ,[[:blank:]] pour correspondre à , après.
  • \([[:alpha:]]\+,[[:blank:]][[:alpha:]]\+[[:blank:]][[:digit:]]\+,[^,]\+$\) correspondra à la partie restante de la ligne (la partie que nous voulons mettre au début).

Ensuite, nous avons \2 \1 pour placer le second groupe en premier, puis un espace puis le premier groupe.

Le fichier original sera sauvegardé sous le nom file.txt.bak, si vous ne voulez pas utiliser uniquement -i au lieu de -i.bak.

** Bien que vous obteniez le résultat souhaité, l'utilisation de Regex/sed ne sera pas la solution optimale dans ce cas.

EDIT: Si vous avez une ligne comme [Internet disconnected] Friday, Mar 13,2015 15:48:34, essayez ceci:

$ sed -i.bak 's_\(.*[^,]\),*[[:blank:]]\([[:alpha:]]\+,[[:blank:]][[:alpha:]]\+[[:blank:]][[:digit:]]\+,[^,]\+$\)_\2 \1_' file.txt 
Friday, Mar 13,2015 15:48:34 [Internet disconnected]
Friday, Mar 13,2015 16:59:42 blah, blah, blah
Friday, Mar 13,2015 16:51:11 yadi, yadi, yada

Dans la regex précédente, nous avions \(.*\),[[:blank:]] (une virgule et un espace après le premier groupe correspondant). Désormais, pour inclure la nouvelle ligne dans la sortie, nous avons créé le premier groupe correspondant \(.*[^,]\) afin de nous en assurer. ne se termine pas par une virgule et ensuite nous avons mis en correspondance ,* c'est-à-dire une ou plusieurs virgules. Ainsi, la nouvelle commande sed fonctionnera pour tous les cas mentionnés.

5
heemayl

avec awk, la syntaxe est beaucoup plus simple

awk -F, '{print $4","$5","$6","$1","$2","$3}' file.txt
4
Panther

La règle d'or des expressions régulières est "Moins, c'est plus". Vous devriez toujours essayer d’écrire la regex la plus simple qui corresponde à vos données. Cela le rend non seulement beaucoup plus facile à lire et à comprendre, mais également beaucoup plus robuste et ne cédera pas aux petites modifications de format. Donc, dans votre cas, vous pourriez simplement faire:

$ sed -r 's/(.*), ([^,]+,[^,]+,[^,]*$)/\2\1/' file 
Friday, Mar 13,2015 16:59:42blah, blah, blah, 
Friday, Mar 13,2015 16:51:11yadi, yadi, yada, 

Cela correspond à tout depuis le début de la ligne jusqu'à une virgule et un espace ((.*),) et, comme le motif est entouré de parenthèses, enregistre sous le nom \1. Maintenant, parce que le deuxième groupe capturé (le deuxième motif entre parenthèses) monte à la fin de la ligne (c'est ce que signifie le $), nous savons que nous faisons correspondre la bonne partie du premier.

Le second recherche un ou plusieurs segments sans virgule ([^,]+), une virgule, un autre jeu de virgules, un autre virgule, puis autant de caractères que possible sans virgule jusqu'à la fin de la ligne. . De cette façon, nous pouvons identifier correctement les derniers champs comme étant la date. Le s/// est l'opérateur de substitution qui change ici simplement l'ordre des premier et deuxième motifs capturés.

Vous pouvez également faire la même chose dans awk. Vraisemblablement, le texte avant la date est variable, nous ne pouvons donc pas supposer le même nombre de champs pour chaque ligne. Par conséquent, nous devrons compter les champs en arrière à partir de la fin de la ligne:

$ awk -F, '{ 
            printf "%s,%s,%s, ", $(NF-2),$(NF-1),$NF; 
            for(i=1;i<NF-3;i++){printf "%s,", $i} print $(NF-3)
         }' file 
 Friday, Mar 13,2015 16:59:42, blah, blah, blah
 Friday, Mar 13,2015 16:51:11, yadi, yadi, yada

Ou en Perl:

$ Perl -lpe 's/(.*), ([^,]+,[^,]+,[^,]*$)/\2, \1/' file 
Friday, Mar 13,2015 16:59:42, blah, blah, blah
Friday, Mar 13,2015 16:51:11, yadi, yadi, yada

$ Perl -F, -lane 'print join ",",@F[$#F-2,$#F-1,$#F,0..$#F-3]' file 
 Friday, Mar 13,2015 16:59:42,blah, blah, blah
 Friday, Mar 13,2015 16:51:11,yadi, yadi, yada
3
terdon