2017-10-21 122 views
1

J'ai écrit un script bash responsable de la «réduction» d'un fichier journal. Étant donné un fichier journal du format:Problèmes de performances avec le script bash

21 Oct 2017 12:38:03 [DEBUG] Single line message 
21 Oct 2017 12:38:05 [DEBUG] Multi line 
message 
that may continue 
several lines 
21 Oct 2017 12:38:07 [DEBUG] Single line message 

Réduire le fichier journal dans un seul fichier revêtu d'un caractère de séparation:

21 Oct 2017 12:38:03 [DEBUG] Single line message 
21 Oct 2017 12:38:05 [DEBUG] Multi line; message; that may continue; several lines 
21 Oct 2017 12:38:07 [DEBUG] Single line message 

Le script bash suivant atteint cet objectif, mais à un rythme lent atrocement . Un journal d'entrée de 500 Mo peut prendre 30 minutes sur une machine à 8 cœurs de 32 Go.

while read -r line; do 

    if [ -z "$line" ]; then 
    BUFFER+=$LINE_SEPERATOR 
    continue 
    done 

    POSSIBLE_DATE='cut -c1-11 <<< $line' 
    if [ "$PREV_DATE" == "$POSSIBLE_DATE" ]; then # Usually date won't change, big comparison saving. 
    if [ -n "$BUFFER" ]; then 
     echo $BUFFER 
     BUFFER="" 
    fi 

    BUFFER+="$line" 
    elif [[ "$POSSIBLE_DATE" =~ ^[0-3][0-9]\ [A-Za-z]{3}\ 2[0-9]{3} ]]; then # Valid date. 
    PREV_DATE="$POSSIBLE_DATE" 
    if [ -n "$BUFFER" ]; then 
     echo $BUFFER 
     BUFFER="" 
    fi 

    BUFFER+="$line" 
    else 
    BUFFER+="$line" 
    fi 
done 

Des idées comment je peux optimiser ce script? Il ne semble pas que la regex soit le goulot d'étranglement (ma première optimisation) car maintenant cette condition est rarement atteinte.

La plupart des lignes du fichier journal sont des lignes simples. Il ne s'agit donc pas d'une comparaison directe des 11 premiers caractères.

Merci.

+2

Utilisez simplement Python. Ce sera tellement mieux que les processus de reproduction chaque fois que vous lisez une ligne. Ou utilisez AWK. –

+0

'POSSIBLE_DATE = 'couper -c1-11 <<< $ line'' à moins qu'il y ait un problème de copier-coller, votre condition ne teste pas ce que vous voulez ... – Mat

Répondre

2

utilisant awk

Il sera beaucoup plus rapide car il ne se reproduit pas plusieurs processus.

$ awk '/^[^0-9]/{ORS="; "} /^[0-9]/{$0=(FNR==1)?$0:RS $0; ORS=""} END{printf RS}1' file 
21 Oct 2017 12:38:03 [DEBUG] Single line message 
21 Oct 2017 12:38:05 [DEBUG] Multi line message; that may continue ; several lines; 
21 Oct 2017 12:38:07 [DEBUG] Single line message 

/^[^0-9]/{ORS="; "}: Si la ligne commence par non-chiffres puis définissez sortie d'enregistrement séparateur comme ; au lieu de défaut \n

/^[0-9]/{$0=(FNR==1)?$0:RS $0; ORS=""}: Si elle commence par un chiffre, puis mis ORS="" et précédez RS ou \n au dossier (à l'exception de la première ligne, c.-à-d. FNR==1 où nous ne voulons pas de retour à la ligne au début)

+1

Merci! Cela fonctionne très bien. J'ai dû modifier l'expression rationnelle pour être un peu plus agressif dans l'analyse des dates de début, mais cela fonctionne brillamment. Merci pour l'explication. – jammmie999

1

Vous pouvez utiliser sed

sed ':B;/^[0-9][0-9]* /N;/\n[0-9][0-9]* /!{s/\n/; /;bB};h;s/\n.*//p;x;s/.*\n//;tB' infile 

Vous pouvez régler la regex '[0-9] [0-9] * à votre besoin.