divida um arquivo, passe cada parte como um parâmetro para um script, execute cada script em paralelo

5

Eu tenho um words.txt com 10000 palavras (uma para uma linha). Eu tenho 5.000 documentos. Eu quero ver quais documentos contêm qual dessas palavras (com um padrão de regex em torno da palavra). Eu tenho um script.sh que greps os documentos e saídas hits. Eu quero (1) dividir meu arquivo de entrada em arquivos menores (2) alimentar cada um dos arquivos para script.sh como um parâmetro e (3) executar tudo isso em paralelo.

Minha tentativa com base no tutorial está atingindo erros

$parallel ./script.sh ::: split words.txt # ./script.sh: line 22: split: No such file or directory

Meu script.sh se parece com isso

#!/usr/bin/env bash

line 1 while read line
line 2  do
        some stuff
line 22 done < $1

Eu acho que eu poderia dividir a saída para um loop de diretório através dos arquivos no diretório de lançamento de comandos grep - mas como pode fazer isso de forma elegante e concisa (usando paralelo)?

    
por bernie2436 29.07.2014 / 02:53

2 respostas

4

Você pode usar a ferramenta split :

split -l 1000 words.txt words-

dividirá seu arquivo words.txt em arquivos com no máximo 1.000 linhas cada nomeado

words-aa
words-ab
words-ac
...
words-ba
words-bb
...

Se você omitir o prefixo ( words- no exemplo acima), split usa x como o prefixo padrão.

Para usar os arquivos gerados com parallel , você pode fazer uso de um glob:

split -l 1000 words.txt words-
parallel ./script.sh ::: words-[a-z][a-z]
    
por 29.07.2014 / 03:15
5

Você provavelmente não precisa dos arquivos temporários, desde que leu o STDIN. Portanto, não há realmente nenhum motivo para usar split . Livrar-se dos arquivos usando --pipe :

cat words | parallel --pipe -L 1000 -N1 ./script.sh

Se realmente é apenas um grep que você quer:

find dir-with-5000-files -type f | parallel -X grep -f words.txt 

Se words.txt for muito grande para caber na memória, você poderá dividir isso:

find dir-with-5000-files -type f | parallel -X "cat words.txt | parallel --pipe grep -f -"

A página man do GNU Parallel aborda a forma mais eficiente de usar grep n linhas para m expressões regulares: link

A solução mais simples para o grep de um arquivo grande para muitos regexps é:

grep -f regexps.txt bigfile

Ou se os regexps forem sequências fixas:

grep -F -f regexps.txt bigfile

Existem 2 fatores limitantes: CPU e E / S de disco. A CPU é fácil de medir: se o grep levar 90% da CPU (por exemplo, ao executar o topo), a CPU será um fator limitante e a paralelização acelerará isso. Caso contrário, a E / S de disco é o fator limitante e, dependendo do sistema de disco, pode ser mais rápido ou mais lento paralelizar. A única maneira de saber com certeza é medir.

Se a CPU é o fator limitante, a paralelização deve ser feita no regexps:

cat regexp.txt | parallel --pipe -L1000 --round-robin grep -f - bigfile

Isso iniciará um grep por CPU e lerá bigfile uma vez por CPU, mas como isso é feito em paralelo, todas as leituras, exceto a primeira, serão armazenadas em cache na RAM. Dependendo do tamanho de regexp.txt, pode ser mais rápido usar --block 10m em vez de -L1000. Se regexp.txt for muito grande para caber na RAM, remova --round-robin e ajuste -L1000. Isso fará com que o bigfile seja lido mais vezes.

Alguns sistemas de armazenamento apresentam um desempenho melhor ao ler vários blocos em paralelo. Isso vale para alguns sistemas RAID e para alguns sistemas de arquivos de rede. Para paralelizar a leitura de bigfile:

parallel --pipepart --block 100M -a bigfile grep -f regexp.txt

Isso dividirá o bigfile em blocos de 100 MB e executará o grep em cada um desses blocos. Para paralelizar a leitura de bigfile e regexp.txt, combine os dois usando --fifo:

parallel --pipepart --block 100M -a bigfile --fifo cat regexp.txt \
\| parallel --pipe -L1000 --round-robin grep -f - {}
    
por 29.07.2014 / 08:53