Comment ajouter la ligne à la ligne précédente?


9

J'ai un fichier journal qui doit être analysé et analysé. Le fichier contient quelque chose de similaire comme ci-dessous:

Fichier:

20141101 server contain dump
20141101 server contain nothing
    {uekdmsam ikdas 

jwdjamc ksadkek} ssfjddkc * kdlsdl
sddsfd jfkdfk 
20141101 server contain dump

Sur la base du scénario ci-dessus, je dois vérifier si la ligne de départ ne contient pas de date ou de numéro que je dois ajouter à la ligne précédente.

Fichier de sortie:

20141101 server contain dump
20141101 server contain nothing {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdl sddsfd jfkdfk 
20141101 server contain dump

Réponses:


11

Une version dans perl, utilisant des lookaheads négatifs:

$ perl -0pe 's/\n(?!([0-9]{8}|$))//g' test.txt
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk
20141101 server contain dump

-0permet à l'expression régulière d'être mise en correspondance dans l'ensemble du fichier , et \n(?!([0-9]{8}|$))est une anticipation négative, ce qui signifie une nouvelle ligne non suivie de 8 chiffres, ou la fin de la ligne (qui, avec -0, sera la fin du fichier).


@terdon, mis à jour pour enregistrer la dernière nouvelle ligne.
muru

Joli! Je vous voterais positivement mais j'ai bien peur de l'avoir déjà eu :)
terdon

Non, -0si pour les enregistrements délimités NUL. Utilisez -0777pour slurper le fichier entier en mémoire (ce dont vous n'avez pas besoin ici).
Stéphane Chazelas

@ StéphaneChazelas Alors, quelle est la meilleure façon de faire correspondre Perl à la nouvelle ligne, à part lire l'intégralité du fichier?
muru

Voir les autres réponses qui traitent le fichier ligne par ligne.
Stéphane Chazelas

5

Peut être un peu facile avec sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • la première partie :1;N;$!b1rassemble toutes les lignes du fichier divisées par \n1 longue ligne

  • la deuxième partie supprime le symbole de nouvelle ligne s'il suit le symbole non numérique avec des espaces possibles entre ses.

Pour éviter la limitation de mémoire (en particulier pour les gros fichiers), vous pouvez utiliser:

sed -e '1{h;d}' -e '1!{/^[0-9]/!{H;d};/^[0-9]/x;$G}' -e 's/\n\+\( *[^0-9]\)/\1/g'

Ou oubliez un sedscript difficile et rappelez-vous que l'année commence2

tr '\n2' ' \n' | sed -e '1!s/^/2/' -e 1{/^$/d} -e $a

Nice, +1. Pourriez-vous ajouter une explication sur la façon dont cela fonctionne, s'il vous plaît?
terdon

1
Aw. Agréable. Je me fais toujours tr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'moi - même.
mirabilos

Désolé, je dois cependant revenir en arrière pour utiliser des choses qui ne sont pas des POSIX BASIC REGULAR EXPRESSION S dans sed (1) , qui est un GNUisme.
mirabilos

1
@Costas, c'est la page de manuel de GNU grep. Les spécifications POSIX BRE sont . BRE équivalent de ERE +est \{1,\}. [\n]n'est pas portable non plus. \n\{1,\}serait POSIX.
Stéphane Chazelas

1
De plus, vous ne pouvez pas avoir une autre commande après une étiquette. : 1;xest de définir l' 1;xétiquette dans les seds POSIX. Donc , vous avez besoin: sed -e :1 -e 'N;$!b1' -e 's/\n\{1,\}\( *[^0-9]\)/\1/g'. Notez également que de nombreuses sedimplémentations ont une petite limite sur la taille de leur espace de motif (POSIX ne garantit que 10 x LINE_MAX IIRC).
Stéphane Chazelas

5

Une façon serait:

 $ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file
 20141101 server contain dump
 20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
 20141101 server contain dump

Cependant, cela supprime également la nouvelle ligne finale. Pour l'ajouter à nouveau, utilisez:

$ { perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file; echo; } > new

Explication

Le -lsupprimera les sauts de ligne (et en ajoutera également un à chaque printappel, c'est pourquoi j'utilise à la printfplace. Ensuite, si la ligne actuelle commence par des nombres ( /^\d+/) et que le numéro de ligne actuel est supérieur à un ( $.>1, cela est nécessaire pour éviter d'ajouter un supplément ligne vide au début), ajoutez un \nau début de la ligne pour printfimprimer chaque ligne.


Alternativement, vous pouvez remplacer tous les \ncaractères par \0, puis changer à nouveau ceux \0qui se trouvent juste avant une chaîne de chiffres \n:

$ tr '\n' '\0' < file | perl -pe 's/\0\d+ |$/\n$&/g' | tr -d '\0'
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
20141101 server contain dump

Pour qu'il ne corresponde qu'à des chaînes de 8 chiffres, utilisez-le à la place:

$ tr '\n' '\0' < file | perl -pe 's/\0\d{8} |$/\n$&/g' | tr -d '\0'

Le premier argument de printfest le format . Utilisationprintf "%s", $_
Stéphane Chazelas

@ StéphaneChazelas pourquoi? Je veux dire, je sais que c'est plus propre et peut-être plus facile à comprendre, mais y a-t-il un danger que cela protège?
terdon

Oui, c'est faux et potentiellement dangereux si l'entrée peut contenir% caractères. Essayez avec une entrée avec %10000000000spar exemple.
Stéphane Chazelas

En C, c'est une très mauvaise source de pratique et de vulnérabilité très connue. Avec perl, echo %.10000000000f | perl -ne printfmet ma machine à genoux.
Stéphane Chazelas

@ StéphaneChazelas wow, oui. Le mien aussi. Assez juste alors, répondez édité et merci.
terdon

3

Essayez de faire cela en utilisant :

#!/usr/bin/awk -f

{
    # if the current line begins with 8 digits followed by
    # 'nothing' OR the current line doesn't start with 8 digits
    if (/^[0-9]{8}.*nothing/ || !/^[0-9]{8}/) {
        # print current line without newline
        printf "%s", $0
        # feeding a 'state' variable
        weird=1
    }
    else {
        # if last line was treated in the 'if' statement
        if (weird==1) {
            printf "\n%s", $0
            weird=0
        }
        else {
            print # print the current line
        }
    }
}
END{
    print # add a newline when there's no more line to treat
}

Pour l'utiliser:

chmod +x script.awk
./script.awk file.txt

2

Un autre moyen plus simple (que mon autre réponse) en utilisant l' algorithme de et terdon :

awk 'NR>1 && /^[0-9]{8}/{printf "%s","\n"$0;next}{printf "%s",$0}END{print}' file

ITYM END{print ""}. Alternative:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
Stéphane Chazelas


0

Le programme en bash:

while read LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo -ne "\n${LINE} "
    else
        echo -n "${LINE} "
    fi
done < file.txt

sous une seule ligne:

while read L; do if [[ $L =~ ^[0-9]{8} ]]; then echo -ne "\n${L} "; else echo -n "${L} "; fi done < file.txt

Solution avec barres obliques inverses préservant ( read -r) et espaces de IFS=début (juste après while):

while IFS= read -r LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo
        echo -nE "\n${LINE} "
    else
        echo -nE "${LINE} "
    fi
done < file.txt

formulaire d'une ligne:

while IFS= read -r L; do if [[ $L =~ ^[0-9]{8} ]]; then echo; echo -nE "${L} "; else echo -nE "${L} "; fi done < file.text

Cela se cassera si la ligne contient, par exemple, une barre oblique inverse et un n. Il supprime également les espaces blancs. Mais vous pouvez utiliser mkshpour cela:while IFS= read -r L; do [[ $L = [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]* ]] && print; print -nr -- "$L"; done; print
mirabilos

Bien sûr, ce n'est pas pour tout l'algorithme, mais une solution pour les exigences fournies par la tâche. Bien sûr, la solution finale sera plus complexe et moins lisible en un coup d'œil, comme cela se produit généralement dans la vraie vie :)
tour

Je suis d'accord, mais j'ai appris la manière difficile de ne pas trop supposer l'OP OP surtout s'ils remplacent le texte réel par du texte factice.
mirabilos

0
[shyam@localhost ~]$ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' appendDateText.txt

Ça marchera

i/p:
##06/12/2016 20:30 Test Test Test
##TestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test test
##i123312331233123312331233123312331233123312331233Test
## 06/12/2016 20:30 abc

o/p:
##06/12/2016 20:30 Test Test TestTestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test ##testi123312331233123312331233123312331233123312331233Test
06/12/2016 20:30 abc vi appendDateText.txt 
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.