É possível no bash começar a ler um arquivo de um deslocamento de contagem de bytes arbitrário?

22

Quero localizar uma data que esteja em algum lugar em um log de 8 GB (texto).

Posso ignorar um pouco uma leitura sequencial completa e primeiro fazer divisões binárias do arquivo (tamanho) ou navegar de alguma forma no sistema de arquivos inodes(sobre o qual sei muito pouco), para começar a ler de cada ponto de divisão, até encontrar um deslocamento adequado de por onde começar minha pesquisa de texto por uma linha que contenha a data?

taila leitura da última linha não usa uma leitura seqüencial normal, então, eu me pergunto se esse recurso está disponível de alguma forma no bash, ou eu precisaria usar Python ou C / C ++ ... mas estou especificamente interessado em uma bashopção ..

Peter.O
fonte
stackoverflow.com/questions/1272675/…
Ciro Santilli escreveu:

Respostas:

8
for (( block = 0; block < 16; block += 1 ))
do 
    echo $block; 
    dd if=INPUTFILE skip=$((block*512))MB bs=64 count=1 status=noxfer 2> /dev/null | \
        head -n 1
done

que .. não cria arquivos de divisão temporária, ignora blocos * 512 MB de dados a cada execução, lê 64 bytes dessa posição e limita a saída à primeira linha desses 64 bytes.

convém ajustar 64 para o que achar necessário.

akira
fonte
@akira .. Isso parece muito bom, mas eu quero ver um pouco mais primeiro .. (então, até amanhã .....
Peter.O
11
@akira .. 'dd' é impressionante. Ele funciona bem com a pesquisa binária dividida ... Agora posso extrair uma linha regex'd (por sua chave Date), de um arquivo 8G classificado em menos de 1 segundo ... Então, parece que vou conseguir meus 3 segundo alvo pessoal para extrair um intervalo de datas entre duas chaves (inclusive) .. excluindo o tempo de saída, que varia dependendo de quanto está sendo produzido .. Também usarei ddpara isso ... É uma ótima ferramenta! :)
Peter.O
30

Parece que você quer:

tail -c +1048576

ou qualquer número de bytes que você deseja pular. O sinal de mais indica a cauda para medir desde o início do arquivo em vez do final. Se você estiver usando a versão GNU do tail, pode escrever isso como:

tail -c +1M

Para obter um número fixo de bytes após o corte, em vez de todo o restante do arquivo, basta passar pela cabeça:

tail -c +1048576 | head -c 1024
Ross Smith
fonte
A flexibilidade do Linux / bash é impressionante (eu definitivamente gastei muito tempo mudando para o Linux). Acabei de aceitar a resposta de akira, mas resolvi isso até avaliar isso de maneira mais completa. ddpula para um byte específico (como faz tail), mas é uma dor que codifica comprimentos de linha desconhecidos e, em seguida, uma chamada para sed para remover as linhas parciais principais ... Parece que a cauda | cabeça pode fazer isso sem dor (tão rápido?) . Eu não entendo como a cabeça pode desligar a torneira na cauda, ​​mas parece :) Deve ser um caso de: Se a cabeça parar de receber, a cauda interrompe o envio (e interrompe a leitura). Deve voltar .. amanhã.
precisa saber é o seguinte
@ fred.bear: tail/ headtambém não consigo adivinhar os comprimentos das linhas. você precisa pular para a posição xe então pode olhar para a esquerda ou direita de x para a próxima \n. não importa como o programa é chamado. portanto, nos dois casos, você pula para x e, em seguida, usa headpara procurar o próximo final de linha à direita.
akira
tail|headoferece a possibilidade de não se preocupar em tudo sobre dd's count = val. Com 'dd', se eu não pegar dados suficientes, é "game over". A flexibilidade dos comprimentos das linhas arbitrárias é grande. Eu escrevi uma função para 'dd' que retorna a linha completa "próxima mais próxima" e seu deslocamento, mas eu preferiria evitar o problema de comprimento. Agora testei o tail | head, e ele inicialmente apresenta um bom desempenho (para compensar = 100 MB), mas diminui drasticamente para levar 2 minutos para um acesso em offset = 8 GB (eu posso awkem 1 minuto) ... então é ótimo para menores de arquivo é .. Obrigado por me fazer ciente da combinação cauda / cabeça :)
Peter.O
2

Eu tentaria algo assim para dividir o log em pedaços de 512MiB para uma análise mais rápida.

split <filename> -b 536870912

Se você estiver procurando pelo arquivo, o seguinte funcionará:

for file in x* ; do
  echo $file
  head -n 1 $file
done

Use essa saída para determinar qual arquivo grep para sua data.

sifusam
fonte
Obrigado, mas é mais lento que uma pesquisa seqüencial. Ter um olhar para os meus comentários aqui unix.stackexchange.com/questions/8121/... (em vez de re-escrever a mesma coisa aqui)
Peter.O
usando 'split', você toca em cada byte uma vez. se você fizer isso, você também pode receber os 8GB inteiros.
akira
@sifusam .. Eu quero fazer uma pesquisa binária dividida (não apenas dividir os arquivos) pt.wikipedia.org/wiki/Binary_search_algorithm ... então foi uma boa resposta para uma pergunta diferente :) .. Obrigado por responder .. +1 para que você obtenha rolando ....
Peter.O
0

Aqui está o meu script, estou procurando a primeira linha onde o primeiro campo corresponde ao meu número. As linhas são classificadas de acordo com o primeiro campo. Uso o dd para verificar a primeira linha de blocos de 128K, depois pulo para o bloco e faço uma pesquisa. Melhora a eficiência, pois o arquivo ultrapassa 1 milhão.

Qualquer comentário ou correção é apreciado!

#!/bin/bash

search=$1;
f=$2;

bs=128;

max=$( echo $(du $f | cut -f1)" / $bs" | bc );
block=$max;
for i in $(seq 0 $max); do
 n=$(dd bs=${bs}K skip=$i if=$f 2> /dev/null| head -2 | tail -1 | cut -f1)
 if [ $n -gt $search ]; then
  block=`expr $i - 1` 
  break;
 fi
done; 
dd bs=${bs}K skip=$block if=$f 2> /dev/null| tail -n +2 | awk -v search="$search" '$1==search{print;exit 1;};$1>search{exit 1;};';

* EDIT * ** grep é muito mais rápido e aceita ainda melhor

user59892
fonte