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
William R
la source

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

muru
la source
@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
Costas
la source
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'
terdon
la source
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
Gilles Quenot
la source
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
Gilles Quenot
la source
ITYM END{print ""}. Alternative:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
Stéphane Chazelas
1
sed -e:t -e '$!N;/\n *[0-9]{6}/!s/\n */ /;tt' -eP\;D
mikeserv
la source
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
tour
la source
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 
Shyam Gupta
la source