Extração de campo rápido com grep

1

O problema

Eu tenho um arquivo de linhas de 32M com o seguinte formato

token^Iname^Iurl$

em que ^I é a sequência de escape , e $ é o fim de linha .

Preciso obter o url correspondente a não mais que 10k correspondências com o campo name . O que eu fiz é

# Get second column
cut -f2 <myFile> |
# Find the word and line number
grep -nwi "<matchWord>" |
# Get just the number
cut -f1 -d ':' |
# Not more than 10k
head -n10000

E depois, para cada entrada do resultado anterior

# Print line number 
sed -n '<number>{p;q}' <myFile>
# Get 3rd field
cut -f3

Agora, esta última operação com sed é ridiculamente lenta. Eu estou querendo saber como obter tudo isso usando grep apenas, ou qualquer outra forma que não diminua a velocidade após as primeiras partidas de 1k.

Idéia

Seria perfeito poder operar grep na linha inteira (sem cut -f2 ), segmentar apenas a segunda coluna e, em seguida, cut -f3 , mas não tenho a menor ideia de como faça isso.

Exemplo

Linha xyz

qwertyuiop^Ibananas are yellow^Ihttp://mignons.cool$

Corresponder a palavra amarelo no campo name - > me dê http://mignons.cool .

cut é necessário, porque não quero corresponder as coisas no campo token e url .

Se eu enviar para grep a cut de myFile , não terei mais acesso ao campo url , no qual estou interessado.

Entrada e saída esperada

Arquivo de entrada:

mxp4EdOy-IXkuwsuOfs0EQ^Ilegal yellow pad paper^I0/3/3031.jpg$
AeS7tgmlVffBhousr9YY5Q^Ihelicopter parking only sign^I0/3/3032.jpg$
8dl-VixSjG4Y0FpX9f5KHA^Iwritten list ^I0/3/3033.jpg$
XYvKZC3D_JSwlY8SPl-zLQ^Ihelicopter parking only road sign^I0/3/3034.jpg$
xF6zpvpHcmfpHP2MmT2FVg^Irun menu windows programming^I0/3/3035.jpg$
mCJvV2rXOmItLBkMZlyIwQ^Icoffee mug^I0/3/3040.jpg$
ZiobHk_dLsN-Q921KPJUTA^Icarpet^I0/3/3197.jpg$
xFrbGOMfVMl0WeqVAcT27A^Iwater jugs^I0/3/3199.jpg$

em que ^I é a sequência de escape , e $ é o fim de linha .

Corresponder palavra helicopter .

Saída esperada (não mais que 10k linhas):

0/3/3032.jpg
0/3/3034.jpg

Solução potencial

Como o campo url contém apenas números, eu pude

cut -f 2,3 <myFile> | grep <matchWord> | cut -f2 | head -n10000

Mas seria melhor usar grep apenas o segundo campo ...

    
por Atcold 26.10.2015 / 03:09

3 respostas

1

Você provavelmente não deve tentar cortar cut . Na verdade, tentar consolidar um pipeline em um único processo para o manuseio de linhas de entrada de 32M provavelmente afetará negativamente o tempo de conclusão geral da tarefa. Isso depende, no entanto, do tipo de computador em que você executa o trabalho.

Se a máquina na qual você processa seus dados tiver vários núcleos de processador, geralmente, consolidar um loop de tarefas em um único processo significa consolidar todo o trabalho em um único núcleo de processador. Isso pode ser desejável em sistemas com apenas um único núcleo de processador ou se o tempo de CPU for precioso, mas, na minha experiência, é melhor saturar um processador e usar todos os núcleos simultaneamente para concluir a tarefa mais cedo.

Dito isto, você definitivamente pode grep apenas o segundo campo:

grep -E $'\t(.* )?yellow( .*)?\t' <infile

... esse padrão corresponderá apenas às cadeias que ocorrem entre dois caracteres de tabulação em uma linha e corresponderá apenas às cadeias que são limitadas em ambos os lados com um espaço ou uma das guias de delimitação de campo. Com o GNU grep você também pode adicionar o parâmetro de correspondência de -m ax para limitar a saída a não mais que 10K correspondências. E então ...

grep -m10000 -E $'\t(.* )?yellow( .*)?\t' <infile | cut -f3

... seria o suficiente para fazer o trabalho todo.

    
por 26.10.2015 / 06:33
2

Existem várias maneiras de fazer isso. Mais fácil é provavelmente com awk

$ awk -F$'\t' '$2 = /helicopter/ {print $3}' input.txt | head -n 10000
0/3/3032.jpg
0/3/3034.jpg
  • -F$'\t' define o separador de campo como TAB
  • $2 = /helicopter/ corresponde apenas ao campo 2
  • print $3 imprime o campo 3 em correspondências

Se você quiser correspondências completas de letras maiúsculas e minúsculas, tente isto:

awk -F$'\t' 'tolower($2) ~ /\<helicopter\>/ { print $3}' input.txt | head -n 10000

Os \< e \> (marcadores de limite de palavras) provavelmente só funcionam em gawk - que é padrão se você estiver no Linux. E observe também que o operador de comparação mudou de = para ~ .

    
por 26.10.2015 / 04:04
2

Você também pode tentar definir localmente o LC_ALL=C em seu ambiente.

Se LC_ALL é uma localidade UTF8, isso pode implicar que o grep precisa fazer a decodificação UTF8 no fluxo de entrada antes de fazer a correspondência, e isso pode diminuir significativamente grep

    
por 26.10.2015 / 09:20