J'ai fait le test suivant et sur mon système, la différence résultante est environ 100 fois plus longue pour le deuxième script.
Mon fichier est une sortie strace appelée bigfile
$ wc -l bigfile.log 
1617000 bigfile.log
Scripts
xtian@clafujiu:~/tmp$ cat p1.sh
tail -n 1000000 bigfile.log | grep '"success": true' | wc -l
tail -n 1000000 bigfile.log | grep '"success": false' | wc -l
xtian@clafujiu:~/tmp$ cat p2.sh
log=$(tail -n 1000000 bigfile.log)
echo "$log" | grep '"success": true' | wc -l
echo "$log" | grep '"success": true' | wc -l
Je n'ai pas de correspondance pour le grep, donc rien n'est écrit dans le dernier tube wc -l
Voici les horaires:
xtian@clafujiu:~/tmp$ time bash p1.sh
0
0
real    0m0.381s
user    0m0.248s
sys 0m0.280s
xtian@clafujiu:~/tmp$ time bash p2.sh
0
0
real    0m46.060s
user    0m43.903s
sys 0m2.176s
J'ai donc exécuté à nouveau les deux scripts via la commande strace
strace -cfo p1.strace bash p1.sh
strace -cfo p2.strace bash p2.sh
Voici les résultats des traces:
$ cat p1.strace 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.24    0.508109       63514         8         2 waitpid
  1.61    0.008388           0     84569           read
  1.08    0.005659           0     42448           write
  0.06    0.000328           0     21233           _llseek
  0.00    0.000024           0       204       146 stat64
  0.00    0.000017           0       137           fstat64
  0.00    0.000000           0       283       149 open
  0.00    0.000000           0       180         8 close
...
  0.00    0.000000           0       162           mmap2
  0.00    0.000000           0        29           getuid32
  0.00    0.000000           0        29           getgid32
  0.00    0.000000           0        29           geteuid32
  0.00    0.000000           0        29           getegid32
  0.00    0.000000           0         3         1 fcntl64
  0.00    0.000000           0         7           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.522525                149618       332 total
Et p2.strace
$ cat p2.strace 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 75.27    1.336886      133689        10         3 waitpid
 13.36    0.237266          11     21231           write
  4.65    0.082527        1115        74           brk
  2.48    0.044000        7333         6           execve
  2.31    0.040998        5857         7           clone
  1.91    0.033965           0    705681           read
  0.02    0.000376           0     10619           _llseek
  0.00    0.000000           0       248       132 open
...
  0.00    0.000000           0       141           mmap2
  0.00    0.000000           0       176       126 stat64
  0.00    0.000000           0       118           fstat64
  0.00    0.000000           0        25           getuid32
  0.00    0.000000           0        25           getgid32
  0.00    0.000000           0        25           geteuid32
  0.00    0.000000           0        25           getegid32
  0.00    0.000000           0         3         1 fcntl64
  0.00    0.000000           0         6           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    1.776018                738827       293 total
Une analyse
Sans surprise, dans les deux cas, la plupart du temps est passé à attendre la fin d'un processus, mais p2 attend 2,63 fois plus longtemps que p1, et comme d'autres l'ont mentionné, vous commencez tard dans p2.sh.
Alors maintenant oubliez le waitpid, ignorez la %colonne et regardez la colonne des secondes sur les deux traces.
Le plus grand temps que p1 passe la plupart de son temps en lecture est probablement compréhensible, car il y a un gros fichier à lire, mais p2 passe 28,82 fois plus en lecture que p1. - bashne s'attend pas à lire un fichier aussi volumineux dans une variable et lit probablement le tampon à la fois, se divise en lignes, puis en obtient un autre.
le nombre de lectures   p2 est de 705k contre 84k pour p1, chaque lecture nécessitant un changement de contexte dans l'espace du noyau et à nouveau. Près de 10 fois le nombre de lectures et de changements de contexte.
Le temps d'écriture p2 passe 41,93 fois plus longtemps en écriture que p1
le nombre d'écritures p1 fait plus d'écrits que p2, 42k vs 21k, mais ils sont beaucoup plus rapides.
Probablement à cause des echolignes dans greppar opposition aux tampons d'écriture de queue.
De plus , p2 passe plus de temps en écriture qu'en lecture, p1 est l'inverse!
Autre facteur Regardez le nombre d' brkappels système: p2 passe 2,42 fois plus de temps qu'il ne le fait à lire! En p1 (il ne s'enregistre même pas). brkest lorsque le programme a besoin d'étendre son espace d'adressage car suffisamment n'a pas été alloué initialement, cela est probablement dû au fait que bash doit lire ce fichier dans la variable, et ne s'attend pas à ce qu'il soit si grand, et comme @scai l'a mentionné, si le le fichier devient trop volumineux, même cela ne fonctionnerait pas.
tailest probablement un lecteur de fichiers assez efficace, car c'est ce pour quoi il a été conçu, il mappe probablement le fichier et recherche les sauts de ligne, permettant ainsi au noyau d'optimiser les E / S. bash n'est pas aussi bon en termes de temps passé à lire et à écrire.
p2 passe 44 ms et 41 ms cloneet execvce n'est pas une quantité mesurable pour p1. Probablement lire bash et créer la variable à partir de la queue.
Enfin, le Totals p1 exécute ~ 150k appels système vs p2 740k (4,93 fois plus).
En éliminant waitpid, p1 passe 0,014416 secondes à exécuter des appels système, p2 0,439132 secondes (30 fois plus).
Il semble donc que p2 passe la plupart du temps dans l'espace utilisateur à ne rien faire, sauf à attendre que les appels système se terminent et que le noyau réorganise la mémoire, p1 effectue plus d'écritures, mais est plus efficace et entraîne une charge système nettement inférieure, et est donc plus rapide.
Conclusion
Je n'essaierais jamais de me soucier du codage via la mémoire lors de l'écriture d'un script bash, cela ne veut pas dire que vous n'essayez pas d'être efficace.
tailest conçu pour faire ce qu'il fait, c'est probablement memory mapsle fichier pour qu'il soit efficace à lire et permette au noyau d'optimiser les E / S.
Une meilleure façon d'optimiser votre problème pourrait être de commencer greppar les lignes de «succès»: puis de compter les vrais et les faux, grepa une option de comptage qui évite encore une fois wc -l, ou mieux encore, de awkdiriger la queue vers et de compter les vraies et fausses simultanément. p2 non seulement prend beaucoup de temps mais ajoute de la charge au système pendant que la mémoire est mélangée avec brks.