Utiliser les commandes sed sur une sortie pour trouver une moyenne

Mon objective est de calculer les temps de ping moyens d’un site Web sur les 10 premiers comptes, la moyenne sur les 10 derniers comptes et la moyenne sur les 10 derniers comptes et de les imprimer d’un seul coup en utilisant le script bash. Jusqu’à présent, j’ai conçu le script suivant:

ping -c 30 google.com > pings.txt sed 's/\=/= /g' pings.txt > formatted_op.txt sed -n '2, 11p' formatted_op.txt > 1.txt sed -n '12, 22p' formatted_op.txt > 2.txt sed -n '22, 32p' formatted_op.txt > 3.txt awk '{print $10}' 1.txt | awk '{ sum += $1 } END {print sum/10}' > 1_avg.txt awk '{print $10}' 2.txt | awk '{ sum += $1 } END {print sum/10}' > 2_avg.txt awk '{print $10}' 3.txt | awk '{ sum += $1 } END {print sum/10}' > 3_avg.txt cat 1_avg.txt 2_avg.txt 3_avg.txt > final_avg.txt cat final_avg.txt rm 1.txt 2.txt 3.txt 1_avg.txt 2_avg.txt 3_avg.txt formatted_op.txt pings.txt 

Cependant, je veux pouvoir le faire sans créer de fichiers temporaires. Comment puis-je le faire? J’ai aussi essayé de le faire en utilisant des tuyaux comme suit:

  ping -c 30 google.com | sed 's/\=/= /g' | sed -n '2, 11p' | awk '{print $10}'| awk '{ sum += $1 } END {print sum/10}' 

mais cela ne calcule que la moyenne des 10 premiers pings et l’utilisation du ping 3 fois donnerait des résultats différents à chaque fois.

Cela peut être fait avec awk assez facilement. En supposant que votre sortie ping ressemble à ceci:

 64 bytes from yyz08s09-in-f110.1e100.net (172.217.1.110): icmp_seq=27 ttl=59 time=0.636 ms 64 bytes from yyz08s09-in-f110.1e100.net (172.217.1.110): icmp_seq=28 ttl=59 time=0.638 ms 64 bytes from yyz08s09-in-f110.1e100.net (172.217.1.110): icmp_seq=29 ttl=59 time=0.658 ms 64 bytes from yyz08s09-in-f110.1e100.net (172.217.1.110): icmp_seq=30 ttl=59 time=0.666 ms 

Ça fera l’affaire:

 ping -c 30 google.com | \ awk ' { split($8,a,"="); if(NR > 1 && NR < 12) { round1+=a[2] } else if (NR < 22) { round2+=a[2] } else if (NR < 32) { round3+=a[2] } } END { print round1/10" "round2/10" "round3/10 } ' 

Nous utilisons la variable NR pour vérifier la ligne de sortie en cours de traitement et ensuite pour incrémenter la variable appropriée. (La valeur est obtenue en divisant le champ de temps sur le signe égal.)

Vous n’avez pas à rendre cela compliqué. Faire quelque chose comme

 for i in {1..10} do ping -c 30 google.com | tail -n1 | awk -v count="$i" -v FS="/" '{print "Count", count,"average : ",$5}' done 

Échantillon sortie

 Count 1 average : 78.484 Count 2 average : 74.473 Count 3 average : 76.971 Count 4 average : 78.789 Count 5 average : 103.609 Count 6 average : 105.754 Count 7 average : 98.969 Count 8 average : 99.009 Count 9 average : 86.186 Count 10 average : 86.521 

Une solution Bash pure n’est pas possible, car Bash ne prend pas en charge l’arithmétique à virgule flottante. Vous avez besoin d’au moins bc .

Pour calculer une moyenne, il n’est pas nécessaire de collecter et de stocker toutes les valeurs. Vous pouvez calculer la moyenne progressivement. Voyez ici ou ici pour les mathématiques ou lisez le volume 2 de ” L’art de la programmation informatique ” de Donald Knuth. La formule en tant que fonction Bash peut ressembler à ceci:

 avg () { local a=$1 # average of the values already seen in the past local x=$2 # new sample local n=$3 # sequence number bc -l <<<"$a + ($x - $a) / $n" } 

Un temps de ping unique peut être collecté par la fonction suivante.

 pingtime () { ping -c1 ${host:-localhost} | sed -n 's/.*time=\(.*\) .*/\1/p' } 

Avec les fonctions ci-dessus, le calcul de n valeurs moyennes avec m échantillons pour chaque moyenne peut être fait avec deux for boucles.

 n=3 m=10 for i in $(seq $n); do a= for j in $(seq $m); do t=$(pingtime) if [ -z "$a" ]; then a=$t else a=$(avg $a $t $j) fi done LC_NUMERIC=C printf '%2g\n' $a done 

Dans la boucle externe, la moyenne a est d'abord effacée. Dans la boucle interne, le temps de ping est mesuré. Si la moyenne est vide, elle est initialisée avec le premier échantillon. Sinon, la moyenne est calculée. Et le résultat est signalé dans la boucle externe.

bc et printf se comportent différemment selon la localisation. Si nécessaire, un comportement spécifique peut être forcé en définissant LC_NUMERIC .

Si vous le souhaitez, vous pouvez append un sleep 1 devant le ping , pour mesurer un seul échantillon par seconde.

Vous pouvez aussi essayer ma solution

 #!/bin/bash pings=10 for i in {1..10}; do ping -c ${pings} google.pl | grep -o 'time=.*' | sed 's/time=\(.*\) ms/\1/g' | awk -v iteration="$i" -v pings="$pings" '{sum+=$1+$2+$3} END {print "Iteration " iteration " Average time " sum/pings}' done