Como evitar que o grep use excessivamente a memória desnecessária

2

Estou ganhando um padrão hexadecimal (atualmente um número) de um arquivo binário grande (1 TB) com

xxd -p /path/to/sda.img | tr -d '\n' | grep -ob '117a0cb17ada1002'

e eu estou querendo saber porque grep usa até 7 GB de RAM e, em seguida, falha com mensagem de erro que não há mais RAM disponível (eu tenho mais (16 GB + 8 GB swap), mas recebo a mensagem). Minhas perguntas são

  • Por que o grep usa tanta memória se não precisa memorizar nada (os resultados da pesquisa são impressos no stdout imediatamente (não há saída, por isso suponho que não há resultados de pesquisa, em um menor os resultados da pesquisa de arquivos foram exibidos imediatamente)?
  • Como eu poderia evitar esse uso de memória se não fosse com grep , em seguida, com um (s) comando (s) alternativo (s) cumprindo a mesma tarefa?

Eu removi \n para ter o deslocamento correto de bytes. Eu gostaria de saber a posição no arquivo / uma linha sem delimitador sem muitos cálculos (número de subtração de novas linhas inseridas, etc.).

Estou executando grep 2.20-3 no Ubuntu 14.10 amd64.

    
por Karl Richter 29.10.2014 / 23:24

1 resposta

7

Você fica sem memória porque grep corresponde linha por linha e removeu explicitamente todas as novas linhas no canal:

$ xxd -p /path/to/sda.img | tr -d '\n' | grep -ob '117a0cb17ada1002'

Eu assumo que o que você quer fazer é encontrar o deslocamento de byte da string hexadecimal específica dentro da saída de xxd , e para ter certeza de que você a encontrou se estiver presente, você remove newlines (a string pode straddle duas linhas).

O script awk a seguir corresponderá a um padrão em linhas consecutivas e imprimirá a posição do primeiro caractere da correspondência no terminal. Ele supõe que a entrada tenha exatamente 60 caracteres de largura (assim como a saída de xxd -p é).

{
    if (NR > 1 && offset = match(line $0, pattern)) {
        printf("%d: %s\n", (NR - 2)*60 + offset, pattern);
    }

    line = $0;
}

Ou, alternativamente (mas de forma equivalente):

NR > 1 {
    if (offset = match(line $0, pattern)) {
        printf("%d: %s\n", (NR - 2)*60 + offset, pattern);
    }
}

{ line = $0 }

Teste-o em dados de entrada aleatórios (as sequências de pesquisa que estou usando mais abaixo estão destacadas nos dados):

$ xxd -p random.dat | head -n 5
b1a632f5218b1404d9873dc20ae80e687c99c618bfc0f92db007c36c2888
21a99d23914e34510b9ab8e1c2b340cf1e4a0585b788aecbbc64f01a7a52
62e1746ca1fa4ff65d575419522d52169c5d3f9eee0e204979d79634db9b
fa78320eb7b9e072adc53720785fc7b65a1ffb04cc77566686ea74ac00fe
f32afc1539690d0046bc13706404d82112442d4bc447ac95df1fe96cd4bd
$ xxd -p random.dat | awk -v pattern=b1a632f5 -f script.awk
1: b1a632f5

$ xxd -p random.dat | awk -v pattern=288821a9 -f script.awk
57: 288821a9

$ xxd -p random.dat | awk -v pattern=ac00fef3 -f script.awk
235: ac00fef3

Executar isso em um arquivo grande de 1 terabyte será lento, não importa o quê. Pode (possivelmente) ser acelerado dando a opção -c 256 a xxd (e alterando 60 a 256 no script awk adequadamente) para reduzir o número de correspondências de padrões, mas os dados em cada linha serão necessariamente ser combinado duas vezes no mesmo (uma vez em conjunto com a linha anterior, e uma vez junto com a próxima linha).

    
por 01.02.2017 / 10:52

Tags