Поиск и запись строки очень большого файла в Bash - PullRequest
0 голосов
/ 17 октября 2018

У меня есть большой csv файл, содержащий 60210 строк.Эти строки содержат хэши, пути и имена файлов, например:

hash                 | path     | number | hash-2      | name 
459asde2c6a221f6...  | folder/..| 6      | 1a484efd6.. | file.txt
777abeef659a481f...  | folder/..| 1      | 00ab89e6f.. | anotherfile.txt
....

Я фильтрую этот файл по списку хэшей, и для облегчения процесса фильтрации я создаю и использую сокращенную версию этогофайл, например, так:

hash                 | path     
459asde2c6a221f6...  | folder/..
777abeef659a481f...  | folder/..

Отфильтрованный результат содержит все строки, которые имеют хэш, которого нет в моей справочной базе хеш-функций.

Но для правильного анализа отфильтрованногорезультат, мне нужны предыдущие данные, которые я удалил.Поэтому моя идея состояла в том, чтобы прочитать отфильтрованный файл результатов, найти поле hash и записать его в расширенный файл результатов, который будет содержать все данные.

Для этого я использую цикл:

getRealNames() {
    originalcontent="$( cat $originalfile)"
    while IFS='' read -r line; do
        hash=$( echo "$line" | cut -f 1 -d " " )
        originalline=$( echo "$originalcontent"  |grep "$hash" )
        if [ ! -z "$originalline" ]; then
            echo "$originalline" > "$resultenhanced"
        fi
    done < "$resultfile"
}

Но при реальном использовании он крайне неэффективен: для предыдущего файла этот цикл занимает примерно 3 часа для работы с 4Go RAM, системой Intel Centrino 2, и мне кажется, что это слишком долго для этоговид операции.

Можно ли как-нибудь улучшить эту операцию?

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Ваше объяснение того, что вы пытаетесь сделать, неясно, потому что оно описывает две задачи: фильтрация данных и затем добавление пропущенных значений обратно в отфильтрованные данные.Ваш пример сценария обращается ко второму, поэтому я предполагаю, что это то, что вы пытаетесь решить здесь.

Пока я читаю, у вас есть отфильтрованный результат, который содержит хэши и пути, и вам нужно найти этихэши в исходном файле, чтобы получить другие значения поля.Вместо загрузки исходного файла в память, просто позвольте grep обработать файл напрямую.Предполагая, что в качестве разделителя полей используется один пробел (как указано cut -d " "), вы также можете извлечь хеш из команды read .

while IFS=' ' read -r hash data; do
    grep "$hash" "$originalfile" >> "$resultenhanced"
done < "$resultfile"
0 голосов
/ 17 октября 2018

Учитывая характер вашего вопроса, трудно понять, почему вы бы предпочли использовать оболочку для обработки такого огромного файла, учитывая специальные инструменты, такие как awk или sed для их обработки.Как Стефан Шазелас указывает на чудесный ответ в Unix.SE .

Ваша задача становится легко решаемой, если вы используете awk / perl, которая ускоряетсядо обработки текста.Кроме того, вы загружаете весь файл в оперативную память, выполняя originalcontent="$( cat $originalfile)", что вовсе нежелательно.

При условии, что как в исходном, так и в справочном файле, hash начинается с первого столбца, а столбцыразделенные |, вам нужно использовать awk как

awk -v FS="|" 'FNR==NR{ uniqueHash[$1]; next }!($1 in uniqueHash)' ref_file orig_file

Приведенные выше попытки сохраняют в памяти только первые записи столбцов из вашего справочного файла, исходный файл не используется вообще.Как только мы используем записи в $1 (первый столбец) эталонного файла, мы фильтруем исходный файл, выбирая те строки, которые не входят в созданный нами массив (uniqueHash).

Изменитьваши locale настройки, чтобы сделать его еще быстрее, установив C локаль как LC_ALL=C awk ...

...