J'ai une situation unique où je peux comparer les solutions proposées sur cette page, et donc j'écris cette réponse comme une consolidation des solutions proposées avec des temps d'exécution inclus pour chacune.
Installer
J'ai un fichier de données texte ASCII de 3,261 gigaoctets avec une paire clé-valeur par ligne. Le fichier contient 3 339 550 350 lignes au total et ne peut pas être ouvert dans n'importe quel éditeur que j'ai essayé, y compris mon go-to Vim. J'ai besoin de sous-définir ce fichier afin d'étudier certaines des valeurs que j'ai découvertes ne commencent qu'aux alentours de la ligne ~ 500 000 000.
Parce que le fichier contient tellement de lignes:
- Je dois extraire uniquement un sous-ensemble des lignes pour faire quelque chose d'utile avec les données.
- La lecture de chaque ligne menant aux valeurs qui m'intéressent va prendre beaucoup de temps.
- Si la solution lit au-delà des lignes auxquelles je tiens et continue à lire le reste du fichier, elle perdra du temps à lire près de 3 milliards de lignes non pertinentes et prendra 6 fois plus de temps que nécessaire.
Mon meilleur scénario est une solution qui extrait une seule ligne du fichier sans lire aucune des autres lignes du fichier, mais je ne peux pas penser à la façon dont j'accomplirais cela dans Bash.
Aux fins de ma raison, je n'essaierai pas de lire les 500 000 000 lignes dont j'avais besoin pour mon propre problème. Au lieu de cela, j'essaierai d'extraire la ligne 50 000 000 sur 3 339 550 350 (ce qui signifie que la lecture du fichier complet prendra 60 fois plus de temps que nécessaire).
J'utiliserai le time
intégré pour comparer chaque commande.
Référence
Voyons d'abord comment la head
tail
solution:
$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0
real 1m15.321s
La ligne de base pour la ligne 50 millions est 00: 01: 15.321, si j'étais allé directement pour la ligne 500 millions, ce serait probablement ~ 12,5 minutes.
Couper
Je doute de celui-ci, mais ça vaut le coup:
$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0
real 5m12.156s
Celui-ci a pris 00: 05: 12.156 pour fonctionner, ce qui est beaucoup plus lent que la ligne de base! Je ne sais pas s'il a lu l'intégralité du fichier ou juste jusqu'à 50 millions de lignes avant de s'arrêter, mais malgré cela, cela ne semble pas être une solution viable au problème.
AWK
J'ai uniquement exécuté la solution avec le exit
car je n'allais pas attendre que le fichier complet s'exécute:
$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0
real 1m16.583s
Ce code a fonctionné en 00: 01: 16.583, ce qui est seulement ~ 1 seconde plus lent, mais toujours pas une amélioration par rapport à la ligne de base. À ce rythme, si la commande exit avait été exclue, il aurait probablement fallu environ 76 minutes pour lire l'intégralité du fichier!
Perl
J'ai également exécuté la solution Perl existante:
$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0
real 1m13.146s
Ce code a fonctionné en 00: 01: 13.146, ce qui est ~ 2 secondes plus rapide que la ligne de base. Si je l'exécutais sur 500 000 000, cela prendrait probablement environ 12 minutes.
sed
La meilleure réponse au tableau, voici mon résultat:
$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0
real 1m12.705s
Ce code a fonctionné en 00: 01: 12.705, ce qui est 3 secondes plus rapide que la ligne de base et ~ 0,4 seconde plus rapide que Perl. Si je l'avais exécuté sur les 500 000 000 lignes, cela aurait probablement pris environ 12 minutes.
mapfile
J'ai bash 3.1 et ne peux donc pas tester la solution mapfile.
Conclusion
Il semble que, pour la plupart, il est difficile d'améliorer la head
tail
solution. Au mieux, la sed
solution offre une augmentation de ~ 3% de l'efficacité.
(pourcentages calculés avec la formule % = (runtime/baseline - 1) * 100
)
Ligne 50 000 000
- 00: 01: 12.705 (-00: 00: 02.616 = -3.47%)
sed
- 00: 01: 13.146 (-00: 00: 02.175 = -2.89%)
perl
- 00: 01: 15.321 (+00: 00: 00.000 = + 0,00%)
head|tail
- 00: 01: 16.583 (+00: 00: 01.262 = + 1.68%)
awk
- 00: 05: 12.156 (+00: 03: 56.835 = + 314.43%)
cut
Ligne 500 000 000
- 00: 12: 07.050 (-00: 00: 26.160)
sed
- 00: 12: 11.460 (-00: 00: 21.750)
perl
- 00: 12: 33.210 (+00: 00: 00.000)
head|tail
- 00: 12: 45.830 (+00: 00: 12.620)
awk
- 00: 52: 01.560 (+00: 40: 31.650)
cut
Ligne 3,338,559,320
- 01: 20: 54.599 (-00: 03: 05.327)
sed
- 01: 21: 24.045 (-00: 02: 25.227)
perl
- 01: 23: 49.273 (+00: 00: 00.000)
head|tail
- 01: 25: 13.548 (+00: 02: 35.735)
awk
- 05: 47: 23.026 (+04: 24: 26.246)
cut
awk
etsed
et je suis sûr que quelqu'un peut également proposer un Perl one-liner;)