Como criar uma fila limitada para tarefas de shell?

5

Eu tenho arquivos de 1000 gzips que eu quero classificar.

Fazendo isso sequencialmente, o procedimento parece bastante simples:

find . -name *.gz -exec zcat {} | sort > {}.txt \;

Não tenho certeza se o código acima funciona (por favor, corrija-me se eu cometi um erro em algum lugar), mas espero que você entenda a ideia.

De qualquer forma, gostaria de paralelizar trabalhos de ungzip / sort para tornar tudo mais rápido. Além disso, não quero ver todos os 1000 processos em execução simultaneamente. Seria ótimo ter uma fila de tarefas limitadas (como BlockingQueue em Java ou BlockingCollection no .NET) com capacidade configurável. Nesse caso, apenas, digamos, 10 processos serão executados em paralelo.

É possível fazer isso no shell?

    
por DNNX 06.12.2011 / 08:54

5 respostas

1

Uma rápida visita ao Google revela essa interessante abordagem: link

for ARG in  $*; do
    command $ARG &
    NPROC=$(($NPROC+1))
    if [ "$NPROC" -ge 4 ]; then
        wait
        NPROC=0
    fi
done
    
por 06.12.2011 / 11:05
4

Use o Paralelo GNU:

find . -name *.gz | parallel --files 'zcat {} | sort' | parallel -X -j1 sort -m {} ';' rm {} > sorted

Você pode instalar o GNU Parallel simplesmente por:

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel

Assista aos vídeos de introdução para saber mais: link e percorra o tutorial (man parallel_tutorial). Você comanda com amor por você.

    
por 06.12.2011 / 15:05
2

Eu escolheria make(1) para essa tarefa - não é shell, mas o make(1) jobserver é quase exatamente o que você queria, e essa tarefa é adequada para as habilidades de make(1) . Observe que a linha que inicia gzip -cd é recuada com um caractere de tabulação. Isso é vital. ( make(1) também pode se sentir um pouco velho às vezes.)

$ cat Makefile 
TXT := $(wildcard *.gz)

all: $(TXT:.gz=.txt)

%.txt:%.gz
    gzip -cd $< | sort > $@
$ cp /usr/share/man/man2/*.gz .
$ ls -l
total 1992
-rw-r--r-- 1 sarnold sarnold  4447 2011-12-06 00:22 aa_change_hat.2.gz
-rw-r--r-- 1 sarnold sarnold  3977 2011-12-06 00:22 aa_change_profile.2.gz
-rw-r--r-- 1 sarnold sarnold  5082 2011-12-06 00:22 accept.2.gz
...
$ time make -j 10
gzip -cd aa_change_hat.2.gz | sort > aa_change_hat.2.txt
gzip -cd aa_change_profile.2.gz | sort > aa_change_profile.2.txt
gzip -cd accept.2.gz | sort > accept.2.txt
gzip -cd accept4.2.gz | sort > accept4.2.txt
gzip -cd access.2.gz | sort > access.2.txt
...
gzip -cd write.2.gz | sort > write.2.txt
gzip -cd writev.2.gz | sort > writev.2.txt

real    0m0.259s
user    0m0.190s
sys 0m0.020s
$ rm w*txt
$ make
gzip -cd wait.2.gz | sort > wait.2.txt
gzip -cd wait3.2.gz | sort > wait3.2.txt
gzip -cd wait4.2.gz | sort > wait4.2.txt
gzip -cd waitid.2.gz | sort > waitid.2.txt
gzip -cd waitpid.2.gz | sort > waitpid.2.txt
gzip -cd write.2.gz | sort > write.2.txt
gzip -cd writev.2.gz | sort > writev.2.txt
$ 

Aviso com o comando rm w*txt de que make(1) inteligentemente faz apenas o mínimo de trabalho necessário para fazer qualquer coisa.

    
por 06.12.2011 / 09:25
0

Com o% GNUxargs, você pode fazer:

xargs -P4 -n 10 -r0a <(find . -name '*.gz' -type f -print0) sh -c '
  for file do
    zcat < "$file" | sort > "$file.txt"
  done' sh {} +

Isso chamaria até 4 sh em paralelo, cada um processando até 10 arquivos um após o outro em um loop.

    
por 22.09.2017 / 18:10
0

Classificando o conteúdo não compactado de muitos arquivos compactados e armazenando o resultado em um arquivo descompactado:

find . -type f -name '*.gz'
    -exec sh -c 'for n; do zcat "$n" | sort -o "$n.txt"; done' sh {} +

Isso executará o for loop

for n; do
    zcat "$n" | sort -o "$n.txt"
done

com o maior número de arquivos possível de uma só vez. Um loop for que não tenha in X irá iterar sobre "$@" por padrão.

O sh -c shell será chamado por find com o máximo de caminhos de arquivo possível (devido ao + em vez de \; no final) e esses caminhos estarão disponíveis para o sh -c shell em $@ .

No seu comando original,

find . -name *.gz -exec zcat {} | sort > {}.txt \;

você tem alguns problemas:

  1. *.gz é sem aspas, o que significa que o shell executará a globalização de nomes de arquivo com os nomes de arquivos no diretório atual.

  2. -exec pode apenas criar um comando simples, não um pipeline.

  3. Você não restringe a arquivos regulares, o que teoricamente significa que você pode pegar um diretório cujo nome é something.gz .

por 22.09.2017 / 13:22