Eu tenho um arquivo de 10Mb preenchido por bytes nulos. Um programa está acessando e altera zeros para strings específicas até o final do arquivo.
Eu tentei usar tail -F | grep wanted_text | grep -v "unwanted_text"
, mas ele não monitora as alterações. Funciona apenas para arquivos de texto comuns, mas não para arquivos preenchidos por zeros.
Todos os bytes nulos são substituídos por linhas separadas por um novo caractere de linha até o final do arquivo. Depois que o arquivo é preenchido, ele está sendo renomeado e o novo é criado.
Então, como eu poderia monitorar as alterações de um arquivo preenchido por bytes nulos com capacidade de filtrar a saída?
Problemas com todo o conceito.
O gravador apenas substitui bytes NUL por outras strings ou pode escrever novas strings sobre strings antigas, possivelmente com sobreposições incompletas? As strings sempre terão pelo menos um separador NUL entre elas?
Ele pode escrever sobre strings com novos NULs para apagar partes do arquivo também?
O arquivo original tem realmente 10 MB de NUL ou é inicialmente um arquivo esparso?
Dado que só podemos encontrar strings lendo o arquivo inteiro, com que frequência você está preparado para fazer isso?
Existe alguma maneira de bloquear o arquivo enquanto ele está sendo gravado, para evitar condições de corrida?
O tamanho do arquivo pode mudar durante toda a operação?
awk (pelo menos, GNU/awk) pode lidar com caracteres NUL e linhas longas. Ele poderia manter uma lista de intervalos que eram NUL (inicialmente apenas [0,10485760]) e verificar se há nova fragmentação nessas regiões. No entanto, isso não detectaria sobregravações. Mas seria capaz de relatar todas as adições sem nenhum processo extra.
GNU/awk tem uma função patsplit() embutida, que corta uma string de acordo com um separador RE, criando um array de campos e um array de separadores. Portanto, o RE /[\000]+/ deve colocar todas as strings em um array e todas as repetições NUL no outro array, e você pode length() cumulativamente para encontrar o deslocamento total no arquivo para cada string. Parece um excelente candidato para investigação.
O comando cat exibe caracteres NUL, a propósito. Você pode vê-los em um arquivo usando o comando od. A razão pela qual eles não aparecem em um terminal é que o driver do terminal os ignora.
Como sugere Romeo, manter um cksum do arquivo anterior lhe dirá se ele mudou, mas não onde. Portanto, pode ser uma otimização útil, dependendo da frequência das atualizações.
Eu fiz o suficiente para verificar se meu conceito de usar GNU/awk com patsplit() é viável. Configurar um Writer falso levou cerca de 70% do tempo de desenvolvimento. Encontrei um conjunto de opções dd que me permitem configurar um arquivo de 10 MB e, em seguida, escrever strings em lugares aleatórios periodicamente.
Eu tenho um Reader que arrasta tudo para a memória como uma longa string e separa os nulos em uma matriz e as strings em outra. Leva 0,044 segundos para ler os 10 MB, 0,989 para dividir a string nas matrizes e 0,138 segundos para relatar o início, o comprimento e o conteúdo das 20 strings que coloquei. Então, cerca de 1,2 segundos para fazer um instantâneo de arquivo.
Todos os tempos feitos no meu laptop barato de 8 anos. Eu acho que, como ele precisa analisar todos os 10 MB de qualquer maneira, ter muito mais strings não afetará tanto o desempenho. O próximo passo é confirmar isso.
Acredito que manter uma tabela de hash antiga e nova das strings e encontrar as alterações será simples e eficiente.
Sabe-se mais sobre a adição de strings aos dados aqui? Se fosse sempre contíguo com os dados anteriores, seria fácil emular a cauda olhando logo após a string anterior. Se não fosse frequente, poderíamos verificar o carimbo de data/hora antes de ler o arquivo. Se estivesse escrevendo um índice na primeira parte do arquivo, poderíamos verificar isso primeiro. Todo o conceito desse arquivo torna difícil ver qual é o uso dele para o resto do sistema - é apenas uma maneira hostil de usar o armazenamento.
Essa pergunta ainda é interessante? Não vejo nenhuma resposta do OP às minhas perguntas anteriores, mas parece-me que as sobreposições de strings e assim por diante só serão exibidas como atualizações e alterações de comprimento.
Eu testei o código para um Writer (para gerar um teste) e um Reader (o script que você pediu), e postarei ambos mais tarde. Este é um teste de 30 segundos para provar que isso funciona. O Writer cria um arquivo de 10 MB quando começa a usar dd e o despeja usando od e relata cada saída como Write Pos. Os dados são uma linha aleatória do arquivo de script. Reader apenas mostra o comprimento e o conteúdo (cada comprimento é 1 menor porque a gravação inclui NL e a leitura descarta NL). Precisamos corrigir a mudança entre arquivos quando você decidir como e quando isso acontece.
Este é o script para o Writer. Ele usa o comando dd para criar o arquivo inicial de uma só vez (se ele não existir) e, em seguida, usa dd para colocar linhas aleatórias de um arquivo de script no arquivo. Costumava fazer isso em posições aleatórias, mas agora coloca cada uma após a anterior. Ele adiciona linhas em intervalos aleatórios calculados em torno de um determinado argumento (2 segundos nesta versão). Ele sai após um limite de tempo específico ou se o arquivo estiver cheio.
Este é o script para o Reader, que deve estar próximo do que você precisa para fingir um comando tail para um arquivo preenchido com NUL. Ele verifica as alterações no arquivo (comparando toda a saída ls -l, que inclui um carimbo de data/hora de nanossegundos) e relata quaisquer adições em um lote. Ele não informa as linhas que já estão no arquivo quando ele é inicializado, apenas as adições enquanto ele está em execução.
Ele funciona em duas velocidades para evitar verificações desperdiçadas. Se detectar quaisquer adições, ele tenta novamente após 1,0 segundos. Se um ciclo não vê adições, ele tenta novamente após 5 segundos (este 5 é um argumento para o processo).