Eu tenho o seguinte em um script de shell:
for file in $local_dir/myfile.log.*;
do
file_name=$(basename $file);
server_name=$(echo $file_name | cut -f 3 -d '.');
file_location=$(echo $file);
mv $file_location $local_dir/in_progress1.log
mysql -hxxx -P3306 -uxxx -pxxx -e "set @server_name='${server_name}'; source ${sql_script};"
rm $local_dir/in_progress1.log
done
Basicamente, ele obtém todos os arquivos em um diretório que corresponde aos critérios, extrai um nome de servidor do nome do arquivo, antes de passá-lo para um script MySQL para processamento.
O que estou pensando é se tenho 10 arquivos que levam 60 segundos cada para serem concluídos e, após 5 minutos, inicio uma segunda instância do script de shell:
- a) o segundo script ainda verá os arquivos que não foram processados
- b) causará problemas em primeira instância se excluir arquivos
ou poderei executá-los em paralelo sem problemas?
Responder1
Seria de supor que “60 segundos” (e até “5 minutos”) é apenas uma boa estimativa e que existe o risco de o primeiro lote ainda estar em andamento quando o segundo lote for iniciado. Se você quiser separar os lotes (e se não houver nenhum problema além dos arquivos de log em uma sobreposição ocasional), uma abordagem melhor seria criar um número de lote como parte da convenção de nomenclatura de arquivos em andamento.
Algo assim:
[[ -s ]] $local_dir/batch || echo 0 > $local_dir/batch
batch=$(echo $local_dir/batch)
expr $batch + 1 >$local_dir/batch
antes do loop for e, em seguida, no início do loop, verifique se o seu padrão corresponde a um arquivo real
[[ -f "$file" ]] || continue
e use o número do lote no nome do arquivo:
mv $file_location $local_dir/in_progress$batch.log
e para frente. Isso reduz o risco de colisão.
Responder2
Há uma resposta acima que fornece algumas boas soluções para o problema, mas pensei em fornecer uma pequena explicação sobre opor quede qual é o problema.
Na maioria das vezes: contanto que seus arquivos de log renomeados (os em andamento) não atendam aos critérios, você estaráprovavelmenteseguro para executar isso commínimorisco. Você ainda receberá alguns erros ...
Sua lista de arquivos é gerada na execução do script. Então o que acabaria acontecendo é que:
Script A
obtém uma lista de 10 files
. Inicia o processamento, 5 files
em (5 restantes) script B
obtém uma lista de 5 remaining files
, inicia o processamento. Script a
em seguida, processa o próximo arquivo de sua lista (que é o mesmo que o arquivo script B
começou a ser processado), ocorrerá um erro porque o arquivo foi renomeado. Portanto, com o tratamento de erros, ele poderia, teoricamente, passar para o próximo em sua lista e funcionar sem problemas. Mas, obviamente, SEMPRE existe a chance de as estrelas se alinharem, mas os scripts atingirem o mesmo arquivo ao mesmo tempo e algo inesperado acontecer. Pese esse risco como quiser.
Uma solução potencialmente mais elegante seria converter isso em um python
script e verificar parallel for loops
qual permitiria criar um único loop for e executá-lo em paralelo, permitindo que um script fizesse o trabalho de dois ou mais.
Responder3
Outra maneira de fazer isso é implementar uma fila em lote simples em seu script.
No início do script, você poderia fazer algo assim:
mkdir -p $localdir/batch
BATCHTMP=$(mktemp batch.XXXXXXXXXX)
MYBATCH="$localdir/batch/batch.$$"
# get list of current log files
find $local_dir/ -name 'myfile.log.*' > "$BATCHTMP"
# exclude any log files already in other batches
grep -vF -f <(sort -u $localdir/batch/batch.*) < "$BATCHTMP" > "$MYBATCH"
rm -f "$BATCHTMP"
# only process log files that are in my batch
for lf in $(cat "$MYBATCH") ; do
....
# somewhere in here, mv or rm the logfile being processed
# so it doesn't get processed again in a later batch run
done
rm -f "$MYBATCH"
É claro que isto é apenas um simples esboço do que precisa ser feito.
Aliás, isso também pode ser feito em um script wrapper que não faz nada além de gerar o arquivo em lote e, em seguida, executar o script principal.