È possibile in bash, iniziare a leggere un file da un offset di conteggio dei byte arbitrario?


22

Voglio trovare una data che si trova da qualche parte in un registro da 8 GB (testo).

Posso in qualche modo bypassare una lettura sequenziale completa e prima fare delle divisioni binarie del file (dimensione), o in qualche modo navigare nel filesystem inodes(di cui ne so molto poco), per iniziare la lettura da ciascun punto di divisione, fino a quando trovo un offset adeguato da da dove iniziare la mia ricerca di testo per una riga che contiene la data?

tailLa lettura dell'ultima riga non usa una normale lettura sequenziale, quindi mi chiedo se questa funzione sia in qualche modo disponibile in bash, o dovrei usare Python o C / C ++ ... ma sono specificamente interessato a bashun'opzione ..


Risposte:


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

che .. non crea file suddivisi in temp, salta blocchi * 512 MB di dati ad ogni esecuzione, legge 64 byte da quella posizione e limita l'output alla prima riga di quei 64 byte.

potresti voler adattare 64 a qualsiasi cosa tu pensi di aver bisogno.


@akira .. Sembra davvero bello, ma voglio guardarlo un po 'di più prima .. (quindi, fino a domani .....
Peter.O

1
@akira .. 'dd' è fantastico. Funziona bene con la ricerca divisa binaria ... Ora posso estrarre una riga regex'd (con il suo tasto Date), da un file 8G ordinato in meno di 1 secondo ... Quindi sembra che raggiungerò i miei 3 secondo obiettivo personale per l'estrazione di un intervallo di date tra due chiavi (incluso) .. escluso il tempo di uscita, che varia a seconda di quanto viene emesso .. Lo userò anche ddper quello ... È un ottimo strumento! :)
Peter.O

30

Sembra che tu voglia:

tail -c +1048576

o qualunque numero di byte si desideri saltare. Il segno più indica a tail di misurare dall'inizio del file anziché dalla fine. Se stai usando la versione GNU di tail puoi scriverla come:

tail -c +1M

Per ottenere un numero fisso di byte dopo il taglio, invece di tutto il resto del file, basta instradarlo attraverso head:

tail -c +1048576 | head -c 1024

La flessibilità di Linux / bash è fantastica (ho sicuramente passato troppo tempo a passare a Linux). Avevo appena accettato la risposta di Akira, ma l'ho tirata fino a quando non lo valuterò più pienamente. ddpassa a un byte specifico (come fa tail), ma è un dolore che codifica intorno a lunghezze di linea sconosciute, quindi una chiamata a sed per eliminare le linee parziali iniziali ... Sembra che tail | head possa farlo indolore (il più velocemente?) . Non capisco come la testa possa chiudere il rubinetto sulla coda, ma sembra :) Dovrebbe essere un caso di: Se la testa smette di ricevere, la coda smette di inviare (e ferma ulteriori letture). Deve tornare .. domani.
Peter

@ fred.bear: tail/ headnon sono in grado di indovinare anche le lunghezze delle linee. devi saltare in posizione x e quindi puoi guardare a sinistra oa destra di x per il prossimo \n. non importa come si chiama il programma. quindi, in entrambi i casi, vai a x e poi usa headper guardare a destra per il prossimo fine della linea.
Akira,

tail|headoffre la possibilità di non essere interessati a tutti su dd's count = val. Con 'dd', se non acquisisco abbastanza dati, è "game over". La flessibilità delle lunghezze arbitrarie delle linee è eccezionale. Ho scritto una funzione per 'dd' che restituisce la riga completa "successiva più vicina" e il suo offset, ma preferirei evitare il problema della lunghezza. Ora ho testato tail | head, e inizialmente si comporta bene (per compensare = 100 MB), ma rallenta drasticamente per impiegare 2 minuti per un accesso a offset = 8 GB (posso awkfarlo in 1 minuto) ... quindi è fantastico per file più piccoli .. Grazie per avermi informato della combinazione coda / testa :)
Peter.O

2

Proverei qualcosa del genere per dividere il registro in blocchi da 512 MiB per l'analisi più rapida.

split <filename> -b 536870912

Se stai cercando il file, funzionerebbe quanto segue:

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

Usa quell'output per determinare quale file grep per la tua data.


Grazie, ma è più lento di una ricerca sequenziale. Dai un'occhiata ai miei commenti qui unix.stackexchange.com/questions/8121/… (piuttosto che riscrivere la stessa cosa qui)
Peter.O

usando 'split' tocchi ogni singolo byte una volta. se lo fai, potresti anche fare il grep dell'intero 8 GB.
Akira,

@sifusam .. Voglio fare una ricerca binaria divisa (non solo dividere i file) en.wikipedia.org/wiki/Binary_search_algorithm ... quindi è stata una buona risposta per una domanda diversa :) .. Grazie per aver risposto .. +1 per iniziare ...
Peter.O

0

Ecco la mia sceneggiatura, sto cercando la prima riga dove il primo campo corrisponde al mio numero. Le linee sono ordinate in base al primo campo. Uso dd per controllare la prima riga di blocchi di 128K, quindi salto al blocco ed eseguo una ricerca. Migliora l'efficienza se il file supera 1 milione.

Qualsiasi commento o correzione è apprezzato!

#!/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 è molto più veloce e ack ancora meglio

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.