Encontrei um comportamento que não entendo ao testar um script que soma as saídas de execuções repetidas de um programa. Para reproduzi-lo crie os arquivos de texto out
, que representa a saída do meu programa, e sum
, o arquivo que contém a soma dos valores retornados nas execuções anteriores e que começa como uma cópia de out
,
cat > out << EOF
2 20
5 50
EOF
cp out sum
A coisa estranha acontece ao correr
paste out sum | awk '{$1 += $3; $2 += $4; NF = 2; print}' | tee sum
várias vezes (15-20 vezes podem ser necessárias). Cada vez que é executado, esse comando deve adicionar os valores nos sum
valores correspondentes out
e gravar os resultados de volta em sum
. O que eu recebo é que ele funciona um número imprevisível de vezes, depois sum
volta para
2 20
5 50
Mais tarde, aprendi que não posso redirecionar ou tee saída para o mesmo arquivo em que estou trabalhando e resolvi o problema usando um arquivo temporário, ainda assim, esse comportamento me deixa perplexo:
por que
… | tee sum
funciona (mesmo que apenas para um número limitado de iterações), enquanto… > sum
nunca sobrescrevesum
?por que não funciona um número previsível de vezes?
Este,
tem uma condição de corrida.
paste
abresum
para ler, etee
abre para escrever, truncando-o. O shell inicia os dois aproximadamente ao mesmo tempo, então cabe ao acaso qual deles abrirá o arquivo primeiro.É claro que na prática, o shell precisa iniciar os utilitários um de cada vez, em alguma ordem específica. Ele provavelmente faz isso da esquerda para a direita, então
paste
pode ter uma chance melhor de ir primeiro, mas isso é um detalhe de implementação e, em qualquer caso, o agendador do SO decide o que é executado e quando.Se
paste
for primeiro, ele abre o arquivo com os dados ainda intactos e provavelmente tem tempo suficiente para ler os dados também. Setee
conseguir abrir o arquivo antespaste
de lê-lo,paste
verá um arquivo vazio.Aqui,
O shell se abre
sum
para escrita, truncando-o. Ele pode fazer isso em paralelo ao startpaste
, mas como truncarsum
não envolve iniciar outro utilitário, provavelmente acontece primeiro. (Não tenho certeza se existe uma regra sobre a ordem de processar redirecionamentos e iniciar os comandos em um pipeline como este, mas não contaria com isso.)Existe uma ferramenta chamada
sponge
para corrigir esse problema (e uma dúzia de perguntas sobre isso ). Ele coleta a entrada que recebe e só a grava depois que a entrada é fechada. Isso deve tersum
atualizado corretamente, sempre: