Executando operações de gravação atômica em um arquivo no bash

13

Depois de examinar a documentação do bash , esta questão e esta ainda não está clara para mim, como posso executar operações de gravação atômica (anexar) em um arquivo no bash. Eu tenho um script que é executado em várias instâncias e em algum momento deve gravar dados em um arquivo:

echo "$RESULT" >> `pwd`/$TEMP_DIR/$OUT_FILE

Como é possível tornar atômicas todas as operações de gravação de todos os scripts em execução simultânea nesse arquivo (para que os dados de uma instância não se sobreponham aos de outra)?

Sebi
fonte
1
Para sua informação, você não precisa `pwd`; você pode apenas usar um ponto ( .). Além disso, você deve citar esse nome de arquivo inteiro, pois inclui variáveis .
Curinga
1
Você também pode querer examinar a possibilidade de usar FIFOs .
Wildcard
@Wildcard Obrigado. Eu estava usando pwdanteriormente no script para notificar o usuário sobre o diretório de trabalho atual e também gravar entradas em um arquivo de log. olhando sobre FIFOs agora.
Sebi 05/04
1
Na verdade, há uma melhor introdução aos FIFOs exatamente neste site; o link que forneci alguns minutos atrás são as especificações do POSIX mkfifoe não o nível introdutório exato.
Wildcard
Seja um arquivo FIFO ou um arquivo, você ainda corre o risco de que duas instâncias gravem ao mesmo tempo e ilumine as saídas uma da outra.
clacke

Respostas:

10

Parece que você precisa usar flockcomo no exemplo do man ( http://linux.die.net/man/1/flock )

(
flock -x 200

# Put here your commands that must do some writes atomically

) 200>/var/lock/mylockfile 

E coloque todos os seus comandos que devem ser atômicos em ().

Sergey Kurenkov
fonte
A operação de gravação será atômica mesmo assim (flock -s 200 # Coloque aqui seus comandos que devem fazer algumas gravações atômicas) 200> / var / lock / mylockfile é lido em vários scripts em execução simultânea? Ou, inversamente, o bloqueio é apenas relativo a / var / lock / mylockfile?
Sebi
1
Seu script bloqueia / var / lock / mylockfile, mas apenas uma instância do seu script pode obter um bloqueio de gravação. Outras instâncias aguardarão até que /var/lock/mylockfileesteja disponível para bloqueio.
Sergey Kurenkov
-s é um bloqueio compartilhado - adequado para leituras; para um bloqueio de gravação, você deve usar -x / -e. Está na página de manual que você vinculou.
Evan Benn #
Em que momento o desbloqueio ocorre neste código? Assim que algo é gravado /var/lock/mylockfileno processo chamado flock -x?
22818 Chris Stryczynski #
@ChrisStryczynski Você pode executar esse exemplo (flock -x 200; date; sleep 10; date;) 200>/var/lock/mylockfileem duas conchas e ver que desbloqueia acontecer somente após todos os comandos em () são executados (que está em um subshell)
Sergey Kurenkov
4

flocké uma das maneiras de operações interligadas. O utilitário faz parte do conjunto de ferramentas util-linux e está disponível apenas para Linux. Outros utilitários, disponíveis em uma ampla variedade de plataformas, baseiam-se no setlockutilitário de Daniel J. Bernstein em seu pacote daemontools:

  • setlock de daemontools
  • setlock do daemontools-encore de Bruce Guenter
  • s6-setlock do s6 de Laurent Bercot
  • chpst do runit de Gerrit Pape
  • runlock do criminoso de Wayne Marshall
  • setlock do meu conjunto de ferramentas nosh

Essas ferramentas operam com um paradigma um pouco diferente do usado na resposta de M. Kurenkov (que flocktambém pode ser empregada, mas não nessa resposta). Um chama o setlockprograma para encadear a carga no comando que deve ser intertravado. setlockele próprio abre e bloqueia o arquivo de bloqueio e deixa um descritor de arquivo aberto em seu processo. O bloqueio persiste enquanto esse processo persistir (a menos que o comando subsequente encadeado para liberar explicitamente o bloqueio localizando e fechando o descritor de arquivo aberto).

Para o caso da pergunta, é necessário bloquear o comando que produz a linha de saída, sabendo que isso chama um externo echo no lugar de um echocomando interno do shell :

setlock mylockfile echo "$ RESULT" >> ./$TEMP_DIR/$OUT_FILE

Nesse caso, não é necessário bloquear a abertura do arquivo de saída no modo de acréscimo. Se fosse, seria necessário abrir esse arquivo dentro do bloqueio, o que exige o uso de programas como fdredir/ redirfd:

setlock mylockfile fdredir --append 1 "./$TEMP_DIR/$OUT_FILE" echo "$ RESULT"
qual deles pode se transformar em uma função shell se quiser:

outfile () {setlock mylockfile fdredir --append 1 "./$TEMP_DIR/$OUT_FILE" "$ @"; } 
[…]
Eco de arquivo "$ RESULT"
ou manter a sintaxe do shell e interpretá-lo por um segundo shell em execução sob o intertravamento, exigindo uma citação não trivial se as variáveis ​​do shell de alguém não forem exportadas como variáveis ​​de ambiente:

setlock mylockfile sh -c 'echo' "$ RESULT" '>> "./'$TEMP_DIR'/'$OUT_FILE'" '

É claro que isso generaliza para outras coisas além de gravar nos arquivos de saída:

setlock mylockfile sh -c '... intertravado; coisa …'

JdeBP
fonte