Comment faire une sum efficace de deux colonnes dans un fichier contenant plus de 270 000 lignes dans bash

J’ai deux colonnes dans un fichier et je veux automatiser la sum des deux valeurs par ligne

par exemple

read write 5 6 read write 10 2 read write 23 44 

Je veux ensuite faire la sum des “read” et “write” de chaque ligne. Après avoir fait la sum, je trouve la sum maximale et place cette valeur maximale dans un fichier. Je pense que je dois utiliser grep -v pour supprimer les en-têtes de colonne par ligne, ce qui, comme indiqué dans les réponses, rend le code inefficace, car je consulte le fichier entier pour lire une ligne.

Je l’ai actuellement dans un script bash (dans une boucle for où $ x est le nom du fichier) pour additionner les colonnes ligne par ligne

 lines=`grep -v READ $x|wc -l | awk '{print $1}'` line_num=1 arr_num=0 while [ $line_num -le $lines ] do arr[$arr_num]=`grep -v READ $x | sed $line_num'q;d' | awk '{print $2 + $3}'` echo $line_num line_num=$[$line_num+1] arr_num=$[$arr_num+1] done 

Cependant, le fichier à additionner contient plus de 270 000 lignes. Le script fonctionne depuis quelques heures maintenant et il n’est pas encore terminé. Existe-t-il un moyen plus efficace d’écrire cela pour qu’il ne soit pas si long?

Utilisez plutôt awk:

 awk 'BEGIN{print "sum" > "outfile"}NR>1{print $1+$2}' infile >> outfile 

Pour la nouvelle source, profitez de la fonction de module:

 awk '!(NR%2){print $1+$2}' infile 

awk est probablement plus rapide, mais la façon idiomatique de faire ceci est quelque chose comme:

 while read -a line; do # read each line one-by-one, into an array # use arithmetic expansion to add col 1 and 2 echo "$(( ${line[0]} + ${line[1]} ))" done < <(grep -v READ input.txt) 

Notez que le fichier d'entrée de fichier n'est lu qu'une seule fois (par grep ) et que le nombre de programmes fourchus en externe est réduit au minimum (juste grep , appelé une seule fois pour tout le fichier d'entrée). Les autres commandes sont des bash builtins.

En utilisant la sous-étape de processus <( ) , au cas où les variables définies dans la boucle while seraient hors de scope de la boucle while. Sinon a | tuyau pourrait être utilisé.

Votre question est assez détaillée, mais votre objective n’est pas clair. La façon dont je le lis, vos chiffres sont sur chaque seconde ligne, et vous voulez seulement trouver la sum maximale. Étant donné que:

 awk ' NR%2 == 1 {next} NR == 2 {max = $1+$2; next} $1+$2 > max {max = $1+$2} END {print max} ' filename 

Vous pouvez également utiliser un pipeline avec des outils qui effectuent une boucle implicite sur l’entrée comme suit:

 grep -v read INFILE | tr -s ' ' + | bc | sort -rn | head -1 > OUTFILE 

Cela suppose qu’il existe des espaces entre vos valeurs de lecture et d’écriture.

Pourquoi ne pas courir:

 awk 'NR==1 { print "sum"; next } { print $1 + $2 }' 

Vous pouvez vous permettre de l’exécuter sur le fichier alors que l’autre script est toujours en cours d’exécution. Il sera complet en quelques secondes au plus (prédiction). Lorsque vous avez confiance, vous pouvez tuer l’autre processus.

Vous pouvez utiliser Perl ou Python au lieu de awk si vous préférez.

Votre code exécute grep , sed et awk sur chaque ligne du fichier d’entrée; c’est extrêmement coûteux. Et ce n’est même pas écrire les données dans un fichier; Il crée un tableau dans la mémoire de Bash qui devra être imprimé plus tard dans le fichier de sortie.

En supposant que ce soit toujours une ligne “en-tête” suivie d’une ligne “données”:

 awk ' BEGIN{ max = 0 } { if( NR%2 == 0 ){ sum = $1 + $2; if( sum > max ) { max = sum } } } END{ print max }' input.txt 

Ou simplement découpez toutes les lignes qui ne sont pas conformes à ce que vous voulez:

 grep '^[0-9]\+\s\+[0-9]\+$' input.txt | awk ' BEGIN{ max = 0 } { sum = $1 + $2; if( sum > max ) { max = sum } } END{ print max }' input.txt