grep -n | sort | sed | cut
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F
Cela devrait fonctionner assez rapidement (certains tests chronométrés sont inclus ci-dessous) avec une entrée de n'importe quelle taille. Quelques notes sur comment:
export LC_ALL=C
- Parce que le point de l'opération suivante est d'obtenir le fichier entier
./F
empilé en ligne avec ./L
le fichier de son lineno, les seuls caractères dont nous devrons vraiment nous soucier sont les [0-9]
chiffres ASCII et les :
deux - points.
- Pour cette raison, il est plus simple de s'inquiéter de trouver ces 11 caractères dans un ensemble de 128 possibles que si l'UTF-8 est autrement impliqué.
grep -n ''
- Cela insère la chaîne
LINENO:
dans la tête de chaque ligne dans stdin - ou <./F
.
sort -t: -nmk1,1 ./L -
sort
néglige de trier ses fichiers d'entrée, et au lieu de cela (correctement) présume qu'ils sont triés et les -m
efface dans l' -numerically
ordre trié, ignorant fondamentalement tout ce qui dépasse tout caractère de côlon -k1,1
se produisant de -t:
toute façon.
- Bien que cela puisse nécessiter un peu d'espace temporaire (selon la distance à laquelle certaines séquences peuvent se produire) , cela ne demandera pas grand-chose par rapport à un tri approprié, et il sera très rapide car il n'implique aucun retour en arrière.
sort
produira un seul flux où n'importe quel lineno ./L
précédera immédiatement les lignes correspondantes ./F
. ./L
Les lignes viennent toujours en premier car elles sont plus courtes.
sed /:/d\;n
- Si la ligne actuelle correspond à
/:/
deux points, d
supprimez-la de la sortie. Sinon, imprimez automatiquement la ligne actuelle et la n
ligne ext.
- Et ainsi de
sed
pruneaux sort
sortie S » à seulement paires de lignes successives qui ne correspondent pas à deux points et la ligne suivante - ou, à seulement une ligne de ./L
puis le lendemain.
cut -sd: -f2-
cut
-s
supprime de la sortie celles de ses lignes d'entrée qui ne contiennent pas au moins une de ses -d:
chaînes d'élimitation - et donc ./L
les lignes sont complètement élaguées.
- Pour les lignes qui le font, leur premier champ
:
délimité par deux -f
points est cut
absent - et il en va de même pour tous grep
les lineno insérés.
petit test d'entrée
seq 5 | sed -ne'2,3!w /tmp/L
s/.*/a-z &\& 0-9/p' >/tmp/F
... génère 5 lignes d'entrée d'échantillon. Alors...
( export LC_ALL=C; </tmp/F \
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
)| head - /tmp[FL]
... impressions ...
==> standard input <==
a-z 1& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/F <==
a-z 1& 0-9
a-z 2& 0-9
a-z 3& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/L <==
1
4
5
de plus grands tests chronométrés
J'ai créé quelques fichiers assez volumineux:
seq 5000000 | tee /tmp/F |
sort -R | head -n1500000 |
sort -n >/tmp/L
... qui a mis 5mil de lignes dedans /tmp/F
et 1,5mil de lignes sélectionnées aléatoirement dans /tmp/L
. J'ai ensuite fait:
time \
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F |wc - l
Il a imprimé:
1500000
grep -n '' \
0.82s user 0.05s system 73% cpu 1.185 total
sort -t: -nmk1,1 /tmp/L - \
0.92s user 0.11s system 86% cpu 1.185 total
sed /:/d\;n \
1.02s user 0.14s system 98% cpu 1.185 total
cut -sd: -f2- \
0.79s user 0.17s system 80% cpu 1.184 total
wc -l \
0.05s user 0.07s system 10% cpu 1.183 total
(J'ai ajouté les barres obliques inverses là-bas)
Parmi les solutions actuellement proposées ici, celle-ci est la plus rapide de toutes, sauf une lorsqu'elle est opposée à l'ensemble de données généré ci-dessus sur ma machine. Parmi les autres, un seul a failli prétendre à la deuxième place, et c'est perl
ici meuh .
Ce n'est en aucun cas la solution originale proposée - elle a chuté d'un tiers de son temps d'exécution grâce aux conseils / inspiration offerts par d'autres. Voir l'historique des publications pour des solutions plus lentes (mais pourquoi?) .
En outre, il convient de noter que certaines autres réponses pourraient très bien lutter si ce n'était pour l'architecture multi-CPU de mon système et l'exécution simultanée de chacun des processus de ce pipeline. Ils travaillent tous en même temps - chacun sur son propre cœur de processeur - en contournant les données et en faisant leur petite partie de l'ensemble. C'est vraiment cool.
mais la solution la plus rapide est ...
Mais ce n'est pas la solution la plus rapide. La solution la plus rapide offerte ici, les mains vers le bas, est le programme C . Je l'ai appelé cselect
. Après l'avoir copié dans mon presse-papiers X, je l'ai compilé comme:
xsel -bo | cc -xc - -o cselect
J'ai ensuite fait:
time \
./cselect /tmp/L /tmp/F |
wc -l
... et les résultats étaient ...
1500000
./cselect /tmp/L /tmp/F \
0.50s user 0.05s system 99% cpu 0.551 total
wc -l \
0.05s user 0.05s system 19% cpu 0.551 total