grep -n | sort | sed | cut
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F
Isso deve funcionar muito rapidamente (alguns testes cronometrados estão incluídos abaixo) com entradas de qualquer tamanho. Algumas notas sobre como:
export LC_ALL=C
- Como o objetivo da operação a seguir é colocar o arquivo inteiro
./F
empilhado em linha com ./L
o arquivo lineno, os únicos caracteres com os quais realmente precisamos nos preocupar são os [0-9]
dígitos ASCII e os :
dois pontos.
- Por esse motivo, é mais simples se preocupar em encontrar esses 11 caracteres em um conjunto de 128 possíveis do que se o UTF-8 estiver envolvido.
grep -n ''
- Isso insere a string
LINENO:
na cabeça de cada linha em stdin - ou <./F
.
sort -t: -nmk1,1 ./L -
sort
negligências para classificar seus arquivos de entrada em tudo, e em vez disso (corretamente) presume que eles são pré-classificados e -m
Erges-los em -numerically
ordem de classificação, ignorando basicamente qualquer coisa além de qualquer possível -k1,1
ocorrendo st -t:
carácter dois pontos de qualquer maneira.
- Embora isso possa exigir algum espaço temporário (dependendo da distância que algumas sequências possam ocorrer) , não exigirá muito em comparação com uma classificação adequada e será muito rápido porque envolve zero retorno.
sort
produzirá um único fluxo no qual qualquer linha de entrada ./L
precederá imediatamente as linhas correspondentes ./F
. ./L
As linhas de sempre vêm primeiro porque são mais curtas.
sed /:/d\;n
- Se a linha atual corresponder a
/:/
dois pontos, d
elimine-a da saída. Senão, imprima automaticamente a n
linha atual e ext.
- E assim
sed
poda sort
a saída apenas para pares de linhas sequenciais que não correspondem a dois pontos e à seguinte linha - ou, apenas a uma linha de ./L
e depois a seguinte.
cut -sd: -f2-
cut
-s
suprime da saída as linhas de entrada que não contêm pelo menos uma de suas -d:
seqüências de elimitadores - e, portanto ./L
, as linhas são completamente removidas.
- Para essas linhas, o primeiro campo
:
delimitado por dois pontos -f
está cut
ausente - e o mesmo ocorre com todos grep
os lineno inseridos.
teste de entrada pequena
seq 5 | sed -ne'2,3!w /tmp/L
s/.*/a-z &\& 0-9/p' >/tmp/F
... gera 5 linhas de entrada de amostra. Então...
( export LC_ALL=C; </tmp/F \
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
)| head - /tmp[FL]
... imprime ...
==> 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
testes cronometrados maiores
Criei alguns arquivos bastante grandes:
seq 5000000 | tee /tmp/F |
sort -R | head -n1500000 |
sort -n >/tmp/L
... que coloca linhas de 5mil e linhas de /tmp/F
1,5mil selecionadas aleatoriamente /tmp/L
. Eu então fiz:
time \
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F |wc - l
Imprimiu:
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
(Eu adicionei as barras invertidas lá)
Entre as soluções atualmente oferecidas aqui, essa é a mais rápida de todas, exceto uma quando comparada com o conjunto de dados gerado acima na minha máquina. Dos outros, apenas um chegou perto de disputar o segundo lugar, e esse é o meuh perl
aqui .
Esta não é de forma alguma a solução original oferecida - ela perdeu um terço do seu tempo de execução graças a conselhos / inspiração oferecidos por outros. Veja o histórico de publicações para soluções mais lentas (mas por quê?) .
Além disso, vale a pena notar que algumas outras respostas podem muito bem apresentar-se melhor se não fossem a arquitetura de várias CPUs do meu sistema e a execução simultânea de cada um dos processos nesse pipeline. Todos eles trabalham ao mesmo tempo - cada um no seu próprio núcleo de processador - passando os dados e fazendo sua pequena parte do todo. É muito legal.
mas a solução mais rápida é ...
Mas não é a solução mais rápida. A solução mais rápida oferecido aqui, mãos para baixo, é o programa C . Eu chamei cselect
. Depois de copiá-lo para minha área de transferência do X, compilei-o como:
xsel -bo | cc -xc - -o cselect
Eu então fiz:
time \
./cselect /tmp/L /tmp/F |
wc -l
... e os resultados foram ...
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