Czy w bash można rozpocząć czytanie pliku z dowolnego przesunięcia liczby bajtów?


22

Chcę zlokalizować datę, która jest gdzieś w dzienniku 8 GB (tekst).

Mogę nieco bypass pełnym sekwencyjnego odczytu i najpierw zrobić binarnych podziałów pliku (rozmiaru), albo jakoś nawigacją systemu plików inodes(co wiem bardzo niewiele o), aby rozpocząć czytanie od każdego punktu podziału, aż znajdę odpowiednie przesunięcie od od czego zacząć szukanie tekstu w wierszu dotyczącym daty?

tailodczyt ostatniego wiersza nie używa normalnego odczytu sekwencyjnego, więc zastanawiam się, czy ta funkcja jest jakoś dostępna w bash, czy też powinienem użyć Pythona lub C / C ++ ... ale jestem szczególnie zainteresowany bashopcją ..


Odpowiedzi:


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

który .. nie tworzy plików podzielonych tymczasowo, pomija bloki * 512 MB danych przy każdym uruchomieniu, odczytuje 64 bajty z tej pozycji i ogranicza wynik do pierwszego wiersza z tych 64 bajtów.

możesz dostosować 64 do tego, co uważasz za potrzebne.


@akira .. Wygląda to naprawdę dobrze, ale najpierw chciałbym spojrzeć na to trochę więcej (tak, do jutra .....
Peter.O

1
@akira .. „dd” jest niesamowity. Działa dobrze z wyszukiwaniem podziału binarnego ... Mogę teraz wyodrębnić wiersz wyrażenia regularnego (według jego klucza daty) z posortowanego pliku 8G w czasie poniżej 1 sekundy ... Więc wygląda na to, że osiągnę moje 3 drugi osobisty cel wyodrębnienia zakresu dat między dwoma kluczami (włącznie) .. z wyłączeniem czasu wyjściowego, który różni się w zależności od tego, ile jest wyprowadzane .. Będę też dddo tego używał ... To świetne narzędzie! :)
Peter.O

30

Brzmi jak chcesz:

tail -c +1048576

lub dowolną liczbę bajtów, które chcesz pominąć. Znak plus informuje ogon, aby mierzył od początku pliku zamiast od końca. Jeśli używasz wersji GNU tail, możesz to napisać jako:

tail -c +1M

Aby uzyskać stałą liczbę bajtów po wycięciu, zamiast całej reszty pliku, po prostu przesuń go przez głowę:

tail -c +1048576 | head -c 1024

Elastyczność Linux / bash jest niesamowita (zdecydowanie spędziłem zbyt długo na przejściu na Linuksa). Właśnie zaakceptowałem odpowiedź Akiry, ale wyciągnąłem ją, dopóki nie dokonuję pełniejszej oceny. ddprzeskakuje do określonego bajtu (jak to robi tail), ale jest to ból kodujący wokół nieznanych długości linii, a następnie wezwanie do sed, aby usunąć wiodące częściowe linie ... Wygląda na to, że głowa ogona może to zrobić bezboleśnie (tak szybko?) . Nie rozumiem, jak głowa może zakręcić kurek na ogonie, ale wydaje się, że tak :) Musi tak być: jeśli głowa przestanie otrzymywać, ogon przestanie wysyłać (i przestanie czytać dalej). Musisz wrócić ... jutro.
Peter.O

@ fred.bear: tail/ headnie są też w stanie ślepo odgadnąć długości linii. musisz przeskoczyć do pozycji x, a następnie możesz spojrzeć w lewo lub w prawo od x, aby zobaczyć następne \n. nie ma znaczenia, jak nazywa się ten program. więc w obu przypadkach przeskakujesz do x, a następnie używasz, headaby spojrzeć w prawo na następny koniec linii.
akira

tail|headoferuje możliwość nie być zainteresowana w ogóle o dd„s count = val. W przypadku „dd”, jeśli nie zdobędę wystarczającej ilości danych, oznacza to „koniec gry”. Elastyczność dowolnych długości linii jest ogromna. Napisałem funkcję dla „dd”, która zwraca „najbliższą” pełną linię i jej przesunięcie, ale wolałbym uniknąć problemu z długością. Testowałem teraz głowę | ogona i początkowo działa ona dobrze (do przesunięcia = 100 MB), ale zwalnia znacznie, aby zająć 2 minuty na jeden dostęp przy przesunięciu = 8 GB (mogę to awkzrobić w ciągu 1 minuty) ... więc jest świetnie za mniejsze pliki .. Dzięki za poinformowanie mnie o kombinacji ogona / głowy :)
Peter.O

2

Spróbowałbym czegoś takiego, aby podzielić dziennik na fragmenty 512 MB dla szybszego parsowania.

split <filename> -b 536870912

Jeśli szukasz pliku, działałyby następujące czynności:

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

Użyj tego wyniku, aby określić, który plik ma być grep dla twojej daty.


Dzięki, ale jest wolniejszy niż wyszukiwanie sekwencyjne. Spójrz na moje komentarze tutaj unix.stackexchange.com/questions/8121/… (zamiast ponownie pisać to samo tutaj)
Peter.O

za pomocą „podziału” dotkniesz każdego bajtu jeden raz. jeśli to zrobisz, możesz po prostu grepować całe 8 GB.
akira

@sifusam .. Chcę przeprowadzić binarne wyszukiwanie podzielone (nie tylko podzielić pliki) en.wikipedia.org/wiki/Binary_search_algorithm ... więc była to dobra odpowiedź na inne pytanie :) .. Dziękuję za odpowiedź .. +1, żebyś
zaczął kręcić

0

Oto mój skrypt, szukam pierwszego wiersza, w którym pierwsze pole pasuje do mojego numeru. Linie są sortowane według pierwszego pola. Używam dd do sprawdzenia pierwszego wiersza bloków o wartości 128K, następnie przeskakuję do bloku i przeprowadzam wyszukiwanie. Poprawia wydajność, gdy plik przekracza 1M.

Wszelkie uwagi i poprawki są mile widziane!

#!/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 jest znacznie szybszy i ACK nawet lepiej

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.