grep -n | sort | sed | cut
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F
Dovrebbe funzionare abbastanza rapidamente (alcuni test a tempo sono inclusi di seguito) con input di qualsiasi dimensione. Alcune note su come:
export LC_ALL=C
- Poiché il punto della seguente operazione è ottenere l'intero file di
./F
stacking in linea con il suo ./L
file lineno, gli unici caratteri di cui dovremo davvero preoccuparci sono le [0-9]
cifre ASCII e i :
due punti.
- Per questo motivo è più semplice preoccuparsi di trovare quegli 11 caratteri in un set di 128 possibili che non se UTF-8 è altrimenti coinvolto.
grep -n ''
- Ciò inserisce la stringa
LINENO:
nella testa di ogni riga in stdin - o <./F
.
sort -t: -nmk1,1 ./L -
sort
trascura di ordinare i suoi file di input, e invece (correttamente) presume che siano preordinati e -m
li rinnova in -numerically
ordine, ignorando praticamente qualsiasi cosa al di là di ogni possibile carattere di due punti -k1,1
presente -t:
.
- Anche se questo può richiedere un po 'di spazio temporaneo (a seconda di quanto distanti possono verificarsi alcune sequenze) , non richiederà molto rispetto a un ordinamento corretto e sarà molto veloce perché comporta zero backtracking.
sort
produrrà un singolo flusso in cui qualsiasi lineno in ./L
precederà immediatamente le corrispondenti linee in ./F
. ./L
Le linee vengono sempre prima perché sono più brevi.
sed /:/d\;n
- Se la riga corrente corrisponde a
/:/
due punti, d
eliminala dall'output. Altrimenti, stampa automaticamente la n
riga corrente ed ext.
- E così
sed
elimina sort
l'output solo per coppie di linee sequenziali che non corrispondono ai due punti e alla seguente riga - o, solo a una linea da ./L
e poi alla successiva.
cut -sd: -f2-
cut
-s
sopprime dall'output quelle delle sue linee di input che non contengono almeno una delle sue -d:
stringhe di eliminazione - e quindi ./L
le linee vengono potate completamente.
- Per quelle righe che lo fanno, il loro primo campo
:
delimitato da due punti -f
è cut
assente - e così va tutto grep
il lineno inserito.
piccolo test di input
seq 5 | sed -ne'2,3!w /tmp/L
s/.*/a-z &\& 0-9/p' >/tmp/F
... genera 5 righe di input di esempio. Poi...
( export LC_ALL=C; </tmp/F \
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
)| head - /tmp[FL]
... stampe ...
==> 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
test a tempo più grandi
Ho creato un paio di file piuttosto grandi:
seq 5000000 | tee /tmp/F |
sort -R | head -n1500000 |
sort -n >/tmp/L
... che inserisce 5mil di linee /tmp/F
e 1,5mil di linee selezionate casualmente in quella /tmp/L
. Ho quindi fatto:
time \
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F |wc - l
Ha stampato:
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
(Ho aggiunto le barre rovesciate lì)
Tra le soluzioni attualmente offerte qui, questa è la più veloce di tutte tranne una se confrontata con il set di dati generato sopra sulla mia macchina. Degli altri solo uno si è avvicinato a contendersi il secondo posto, e questo è meuh perl
qui .
Questa non è affatto la soluzione originale offerta: ha perso un terzo dei suoi tempi di esecuzione grazie ai consigli / ispirazioni offerti da altri. Vedi la cronologia dei post per soluzioni più lente (ma perché?) .
Inoltre, vale la pena notare che alcune altre risposte potrebbero contendere meglio se non fosse per l'architettura multi-CPU del mio sistema e l'esecuzione simultanea di ciascuno dei processi in quella pipeline. Funzionano tutti allo stesso tempo - ognuno sul proprio core del processore - passando attorno ai dati e facendo la loro piccola parte del tutto. È molto bello.
ma la soluzione più veloce è ...
Ma non è la soluzione più veloce. La soluzione più veloce offerto qui, mani verso il basso, è il programma di C . L'ho chiamato cselect
. Dopo averlo copiato negli appunti X, l'ho compilato come segue:
xsel -bo | cc -xc - -o cselect
Ho quindi fatto:
time \
./cselect /tmp/L /tmp/F |
wc -l
... e i risultati sono stati ...
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