Explicação do comportamento imprevisível do tee

1

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

O estranho acontece na corrida

paste out sum | awk '{$1 += $3; $2 += $4; NF = 2; print}' | tee sum

várias vezes (15-20 vezes pode ser necessário). Sempre que for executado, esse comando deverá adicionar aos valores em sum os valores correspondentes em out e gravar os resultados novamente em sum . O que eu obtenho é que ele funciona um número imprevisível de vezes, então sum é revertido para

2 20
5 50

Mais tarde aprendi que Eu não posso redirecionar ou tee saída para o mesmo arquivo que estou trabalhando e resolvi o problema usando um arquivo temporário, ainda assim, esse comportamento me deixa confuso:

  • por que … | tee sum funciona (mesmo que apenas para um número limitado de iterações), enquanto … > sum nunca sobrescreve sum ?

  • por que não funciona um número previsível de vezes?

por Arch Stanton 26.08.2018 / 19:21

1 resposta

6

Isto,

paste out sum | awk ... | tee sum

tem uma condição de corrida. paste abre sum para lê-lo e tee abre para escrever, truncando-o. O shell inicia ambos aproximadamente ao mesmo tempo, então é a chance de 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. Provavelmente faz isso da esquerda para a direita, então paste pode ter uma chance maior de ser o primeiro, mas esse é um detalhe de implementação e, em qualquer caso, o agendador de SO decide o que é executado quando.

Se paste for o primeiro, ele abrirá o arquivo com os dados ainda intactos e, provavelmente, terá tempo suficiente para ler os dados também. Se tee conseguir abrir o arquivo antes que paste o tenha lido, então paste verá um arquivo vazio.

Aqui,

paste out sum | awk ... > sum

O shell abre sum para gravação, truncando-a. Ele pode fazer isso em paralelo ao início de paste , mas como truncar sum não envolve iniciar outro utilitário, isso provavelmente acontece primeiro. (Eu não tenho certeza se há uma regra sobre a ordem de processamento de redirecionamentos e iniciar os comandos em um pipeline como este, mas eu não contaria com isso.)

Há uma ferramenta chamada sponge para corrigir esse problema (e uma dúzia de perguntas sobre ele ). Ele coleta a entrada que recebe e apenas grava após a entrada ser fechada. Isso deve ter sum atualizado corretamente, sempre:

paste out sum | awk ... | sponge sum
    
por 26.08.2018 / 20:52