Возможно ли в bash начать чтение файла с произвольного смещения байтов?

22

Я хочу найти дату, которая находится где-то в журнале 8 ГБ (текст).

Могу ли я несколько обойти полное последовательное чтение и сначала выполнить двоичное разбиение файла (размер) или каким-либо образом перемещаться по файловой системе inodes(о которой я очень мало знаю ), чтобы начать чтение с каждой точки разделения, пока не найду подходящее смещение от с чего начать мой текстовый поиск строки, содержащей дату?

tailчтение последней строки не использует обычное последовательное чтение, поэтому мне интересно, доступна ли эта возможность в bash, или мне нужно использовать Python или C / C ++ ... но меня особенно интересует bashопция ..

Peter.O
источник
stackoverflow.com/questions/1272675/…
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件

Ответы:

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

которая .. не создает файлов временного разделения, пропускает блоки * 512 МБ данных при каждом запуске, считывает 64 байта из этой позиции и ограничивает вывод первой строкой из этих 64 байтов.

Вы можете настроить 64 на то, что вам нужно.

Акира
источник
@akira .. Это выглядит действительно хорошо, но я хочу сначала взглянуть на это немного подробнее .. (так, до завтра .....
Peter.O
1
@akira .. 'дд' это круто. Он хорошо работает с бинарным поиском с разделением ... Теперь я могу извлечь строку регулярного выражения (по ключу даты) из отсортированного файла 8G менее чем за 1 секунду ... Так что, похоже, я достигну своего 3 вторая личная цель для извлечения диапазона дат между двумя ключами (включительно) .. за исключением времени вывода, которое варьируется в зависимости от того, сколько выводится .. Я тоже буду использовать ddдля этого ... Это отличный инструмент! :)
Peter.O
30

Похоже, вы хотите:

tail -c +1048576

или любое количество байтов, которое вы хотите пропустить. Знак плюс говорит хвосту измерять от начала файла вместо конца. Если вы используете GNU-версию tail, вы можете написать это как:

tail -c +1M

Чтобы получить фиксированное количество байтов после вырезки, вместо всего остального файла, просто передайте его через заголовок:

tail -c +1048576 | head -c 1024
Росс Смит
источник
Гибкость Linux / bash потрясающая (я определенно слишком долго переключался на Linux). Я только что принял ответ Акиры, но я потянул его, пока не оценил это более полно. ddпереходит к определенному байту (как и делает tail), но это кодирование боли с неизвестной длиной строки, а затем вызов sed для удаления начальных частичных строк ... Похоже, что tail | head может сделать это безболезненно (так быстро?) , Я не понимаю, как голова может закрыть кран на хвосте, но, похоже, так оно и есть :). Это должно быть так: если голова перестает получать, хвост прекращает посылку (и прекращает дальнейшее чтение). Должен пойти .. завтра.
Peter.O
@ fred.bear: tail/ headтакже не могут слепо угадать длину строки. Вы должны перейти к позиции х, а затем вы можете посмотреть влево или вправо от х для следующего \n. не имеет значения, как называется программа. Итак, в обоих случаях вы переходите к x, а затем используете, headчтобы посмотреть направо для следующего конца строки.
Акира
tail|headпредлагает возможность вообще не беспокоиться по поводу ddcount = val. С 'dd', если я не получаю достаточно данных, это "игра окончена". Гибкость произвольных длин линий велика. Я написал функцию для 'dd', которая возвращает «следующую ближайшую» полную строку и ее смещение, но я бы предпочел избежать проблемы длины. Я сейчас проверил tail | head, и он изначально хорошо работает (для смещения = 100 МБ), но резко замедляется, чтобы занять 2 минуты для одного доступа со смещением = 8 ГБ (я могу сделать awkэто за 1 минуту) ... так что это здорово за файл меньшего размера .. Спасибо, что
сообщили
2

Я бы попробовал что-то вроде этого, чтобы разбить журнал на 512MiB куски для более быстрого анализа.

split <filename> -b 536870912

Если вы ищете файл, будет работать следующее:

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

Используйте этот вывод, чтобы определить, какой файл grep для вашей даты.

sifusam
источник
Спасибо, но это медленнее, чем последовательный поиск. Посмотрите мои комментарии здесь unix.stackexchange.com/questions/8121/… (вместо того, чтобы переписывать то же самое здесь)
Peter.O
используя «split», вы касаетесь каждого байта один раз. если вы сделаете это, вы можете просто собрать все 8 ГБ.
Акира
@sifusam .. Я хочу сделать двоичный поиск (не просто разделить файлы) en.wikipedia.org/wiki/Binary_search_algorithm ... так что это был хороший ответ на другой вопрос :) .. Спасибо за ответ .. +1, чтобы ты катился ....
Peter.O
0

Вот мой сценарий, я ищу первую строку, в которой первое поле соответствует моему номеру. Строки отсортированы по первому полю. Я использую dd для проверки первой строки блоков 128K, затем перехожу к блоку и выполняю поиск. Это повышает эффективность файла более 1М.

Любые комментарии или исправления приветствуются!

#!/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 гораздо быстрее и извед еще лучше

user59892
источник