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
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?
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
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ê.
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.
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.
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:
*.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.
-exec
pode apenas criar um comando simples, não um pipeline.
Você não restringe a arquivos regulares, o que teoricamente significa que você pode pegar um diretório cujo nome é something.gz
.
Tags command-line find sort shell parallelism