AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 554061
Accepted
Gryu
Gryu
Asked: 2019-11-26 06:32:21 +0800 CST2019-11-26 06:32:21 +0800 CST 2019-11-26 06:32:21 +0800 CST

Como monitorar as alterações no arquivo preenchido por bytes nulos?

  • 772

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?

linux files
  • 5 5 respostas
  • 290 Views

5 respostas

  • Voted
  1. Paul_Pedant
    2019-11-26T17:17:54+08:002019-11-26T17:17:54+08:00

    Problemas com todo o conceito.

    1. 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?

    2. Ele pode escrever sobre strings com novos NULs para apagar partes do arquivo também?

    3. O arquivo original tem realmente 10 MB de NUL ou é inicialmente um arquivo esparso?

    4. Dado que só podemos encontrar strings lendo o arquivo inteiro, com que frequência você está preparado para fazer isso?

    5. Existe alguma maneira de bloquear o arquivo enquanto ele está sendo gravado, para evitar condições de corrida?

    6. 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.

    • 1
  2. Paul_Pedant
    2019-11-27T16:44:08+08:002019-11-27T16:44:08+08:00

    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.

    • 1
  3. Paul_Pedant
    2019-12-03T13:55:19+08:002019-12-03T13:55:19+08:00

    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.

    Paul--) ~/wdog -w 1m; ./Reader & sleep 3; ./Writer & jobs; wait
    [1] 25030
    21:40:00.032622643: READER Begins
    21:40:00.059864870: No file: ./myNullFile
    [2] 25066
    [1]-  Running                 ./Reader &
    [2]+  Running                 ./Writer &
    1+0 records in
    1+0 records out
    10485760 bytes (10 MB, 10 MiB) copied, 0.0421849 s, 249 MB/s
    0000000  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00  00
            nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
    *
    10485760
    21:40:03.155837791: WRITER Begins
    21:40:03.166173417: Reading: ./myNullFile
    Initial NUL is at 0
    21:40:04.053667668: Write Pos          0 Lth  32 Txt :print Tx[Row] > Wk; close (Wk);:
    21:40:04.221015418  31 :print Tx[Row] > Wk; close (Wk);:
    21:40:07.453403168: Write Pos         32 Lth  26 Txt :Row = int (nTx * rand());:
    21:40:08.320950327: Write Pos         58 Lth  26 Txt :Row = int (nTx * rand());:
    21:40:08.331308212  25 :Row = int (nTx * rand());:
    21:40:08.331308212  25 :Row = int (nTx * rand());:
    21:40:11.526810010: Write Pos         84 Lth  42 Txt :n = patsplit (buf, Str, /[^\000]+/, Sep);:
    21:40:12.444730213  41 :n = patsplit (buf, Str, /[^\000]+/, Sep);:
    21:40:12.998876406: Write Pos        126 Lth  51 Txt :function Monitor (Local, rs, tk, Buf, Now, P, Q) {:
    21:40:13.474633987  50 :function Monitor (Local, rs, tk, Buf, Now, P, Q) {:
    21:40:14.604161200: Write Pos        177 Lth  43 Txt :Parser( Q, Buf); Differ( Q, P); Now = "P";:
    21:40:15.529375510  42 :Parser( Q, Buf); Differ( Q, P); Now = "P";:
    21:40:18.381688129: Write Pos        220 Lth  44 Txt :cmd | getline ts; close (cmd); return (ts);:
    21:40:18.611992430  43 :cmd | getline ts; close (cmd); return (ts);:
    21:40:19.583131365: Write Pos        264 Lth  19 Txt :split ("", X, FS);:
    21:40:19.642066181  18 :split ("", X, FS);:
    21:40:20.670745471: Write Pos        283 Lth   2 Txt :}:
    21:40:21.701154684   1 :}:
    21:40:23.060019472: Write Pos        285 Lth   5 Txt :done:
    21:40:23.749500189   4 :done:
    21:40:25.956196125: Write Pos        290 Lth   2 Txt :}:
    21:40:26.832889643   1 :}:
    21:40:27.098544055: Write Pos        292 Lth  19 Txt :split ("", X, FS);:
    21:40:27.861066600  18 :split ("", X, FS);:
    21:40:30.707892127: Write Pos        311 Lth  32 Txt :print Tx[Row] > Wk; close (Wk);:
    21:40:30.939979744  31 :print Tx[Row] > Wk; close (Wk);:
    21:40:33.705042808: Write Pos        343 Lth  15 Txt :Reader 30 10 &:
    21:40:34.010378554  14 :Reader 30 10 &:
    21:40:35.103897039: Write Pos        358 Lth   2 Txt :}:
    21:40:36.063585547   1 :}:
    21:40:38.661280841: Write Pos        360 Lth  49 Txt :printf ("%s: Write Pos %10d Lth %3d Txt :%s:\n",:
    21:40:38.674634657: WRITER Exits
    21:40:39.144828936  48 :printf ("%s: Write Pos %10d Lth %3d Txt :%s:\n",:
    21:40:41.188952035: READER Exits
    [1]-  Done                    ./Reader
    [2]+  Done                    ./Writer
    
    • 1
  4. Paul_Pedant
    2019-12-03T14:16:54+08:002019-12-03T14:16:54+08:00

    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.

    #! /bin/bash
    
    #.. Declare the shared file.
    FILE="./myNullFile"
    SIZE=$(( 10 * 1024 * 1024 ))
    
    #.. Using script as source of the strings.
    TEXT="./isNulls"
    WORK="./myLine"
    
    #### Simulate the file writer defined in the question.
    
    function Writer {
    
        local RUN="${1:-60}" SLEEP="${2:-5}"
    
        local AWK='''
    BEGIN { printf ("%s: WRITER Begins\n", TS( )); }
    function Begin (Local) {
        Pos = getNull( );
        printf ("Initial NUL is at %d\n", Pos);
        if (Pos < 0) exit;
    }
    function TS (Local, cmd, ts) {
        cmd = "date \047+%H:%M:%S.%N\047";
        cmd | getline ts; close (cmd); return (ts);
    }
    function Wait (secs) {
        system (sprintf ("sleep %s", secs));
    }
    function getNull (Local, rs, Buf) {
        rs = RS; RS = "^$";
        getline Buf < Fn; close (Fn);
        RS = rs;
        return (-1 + index (Buf, "\000"));
    }
    function Str (Local, Row, Lth) {
        Row = int (nTx * rand());
        Lth = length (Tx[Row]);
        if (Pos + Lth >= Sz) {
            printf ("%s is full: Pos %d, Lth %d, Sz %d\n", Fn, Pos, Lth, Sz);
            exit;
        }
        printf ("%s: Write Pos %10d Lth %3d Txt :%s:\n",
            TS( ), Pos, 1 + Lth, Tx[Row]);
        print Tx[Row] "\n" > Wk; close (Wk);
        system (sprintf (Fmt, Pos, 1 + Lth, Wk, Fn, Wk));
        Pos += 1 + Lth;
    }
    NR == 1 { Fmt = $0; srand (); next; }
    NR == 2 { Fn = $0; next; }
    NR == 3 { Sz = $0; next; }
    NR == 4 { Wk = $0; Begin( ); next; }
    NF { sub (/^[ \011]+/, ""); Tx[nTx++] = $0; next; }
    { Str( ); }
    END { printf ("%s: WRITER Exits\n", TS( )); }
    '''
        local EXPIRED=$(( SECONDS + RUN ))
        local AWK_WT='BEGIN { srand(); } { print 0.1 + 2 * $1 * rand(); }'
        {
            DD_OPT='status=none conv=notrunc bs=1 seek="%s" count="%s"'
            DD_FNS='if="%s" of="%s" && rm -f "%s"'
    
            echo "dd ${DD_OPT} ${DD_FNS}"
            echo "${FILE}"; echo "${SIZE}"; echo "${WORK}"
            awk NF "${TEXT}"
            while (( SECONDS <= EXPIRED )); do
                sleep "$( echo "${SLEEP}" | awk "${AWK_WT}" )"
                echo ''
            done
        } | awk -f <( echo "${AWK}" )
    }
    
    #### Script Body Starts Here.
    
        [[ -r "${FILE}" ]] || {
            dd count=1 bs="${SIZE}" if="/dev/zero" of="${FILE}"
            od -A d -t x1a "${FILE}"
        }
    
        Writer 32 2
    
    • 1
  5. Best Answer
    Paul_Pedant
    2019-12-03T14:39:45+08:002019-12-03T14:39:45+08:00

    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).

    #! /bin/bash
    #: Reader: tail -f a file which is pre-formatted with many trailing NUL characters.
    
    #### Implement the User Requirement.
    
    function Reader {
    
        local RUN="${1:-60}" SLEEP="${2:-5}" FILE="${3:-/dev/null}"
    
        local AWK='''
    BEGIN { NUL = "\000"; }
    function Tick (Local, cmd, ts) {
        cmd = "date \047+%s\047";
        cmd | getline ts; close (cmd); return (ts);
    }
    function TS (Local, cmd, ts) {
        cmd = "date \047+%H:%M:%S.%N\047";
        cmd | getline ts; close (cmd); return (ts);
    }
    function Wait (secs) {
        system (sprintf ("sleep %s", secs));
    }
    function isChange (Local, cmd, tx) {
        cmd = sprintf ("ls 2>&1 -l --full-time \047%s\047", Fn);
        cmd | getline tx; close (cmd);
        if (tsFile == tx) return (0);
        tsFile = tx;
        if (index (tx, "\047")) {
            if (fSt != "B") { fSt = "B"; printf ("%s: No file: %s\n", TS( ), Fn); }
        } else {
            if (fSt != "G") { fSt = "G"; printf ("%s: Reading: %s\n", TS( ), Fn); }
        }
        return (1);
    }
    function atNul (buf, Local, j) {
        j = index (buf, NUL);
        return ((j > 0) ? j : 1 + length (buf)); 
    }
    function List (tx, Local, ts, X, j) {
        sub ("\012$", "", tx); split (tx, X, "\012");
        ts = TS( );
        for (j = 1; j in X; ++j) {
            printf ("%s %3d :%s:\n", ts, length (X[j]), X[j]);
        }
    }
    function Monitor (Local, rs, tk, Buf, Now, End) {
        printf ("%s: READER Begins\n", TS( ));
        tk = Tick( ); Expired = tk + Run;
        Now = -1;
        while (Tick( ) <= Expired) {
            if (! isChange( )) { Wait( Sleep); continue; }
            rs = RS; RS = "\000";
            Buf = ""; getline Buf < Fn; close (Fn);
            RS = rs;
            if (Now < 0) Now = atNul( Buf);
            End = atNul( Buf);
            List( substr (Buf, Now, End - Now));
            Now = End;
            Wait( 1.0);
        }
        printf ("%s: READER Exits\n", TS( ));
    }
    NR == 1 { Run = $0; next; }
    NR == 2 { Sleep = $0; next; }
    NR == 3 { Fn = $0; }
    END { Monitor( Fn); }
    '''
        {
            echo "${RUN}";
            echo "${SLEEP}";
            echo "${FILE}";
    
        } | awk -f <( echo "${AWK}" )
    }
    
    #### Script Body Starts Here.
    
        Reader 40 5 "./myNullFile"
    
    • 1

relate perguntas

  • Inicie/pare o serviço systemd usando o atalho de teclado [fechado]

  • du/df e ls relatando diferentes usos de disco

  • Necessidade de algumas chamadas de sistema

  • astyle não altera a formatação do arquivo de origem

  • Passe o sistema de arquivos raiz por rótulo para o kernel do Linux

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve