Разделить большие файлы на несколько файлов в unix

Мне нравятся оба ответа (SQL, AWK ), потому что они являются стандартными языками и реализуют алгоритм идиоматически. Я собирался еще больше понизить вопрос, потому что он, кажется, настаивает на конкретном решении.

Но именно в этом проблема fgdark :вы слишком полагаетесь на утилиты и не понимаете (алгоритма ).

How could I pipe together cut, grep, numaverage in this manor (other commands may help such as cat, uniq, wc)?

Вы можете «легко» найти конвейерное решение, начав с временных файлов, а когда цепочка заработает, вы объедините их вместе. Результат будет неудовлетворительным. У меня нет numaverage, для начала. Производительность также.

Здесь необходим какой-то «настоящий» язык программирования, например SQL или AWK. Или perl, или python, или C. Или bash -, но bash как скриптовый язык с ассоциативными массивами и арифметическими операторами , а не bash "только" как оболочка.

Вам не нужна усредненная()функция , как в случае решения SQL, вы можете просто считать, складывать и делить данные, которые вы прочитали, в свои массивы.

Поэтому, если вы хотите создавать дополнительные отчеты по данным, вам следует выбрать язык . SQL немного особенный, он хорошо подходит для анализа данных . Другие упомянутые (awk, perl, python, C, даже bash )сильны в данных обработке(манипулировании переменными/массивами/структурами ). Но есть большое совпадение.


Несколько лет я работал с SQL; так я бы сохранил запрос и его вызов:

SQL='select a as Name, count(*) as Count, avg(b) as Average' 
SQL+=' from file'
SQL+=' group by a'

csvsql -tSH --query $SQL file | csvformat -T

'Это не критика, а попытка проиллюстрировать, как структурирован-язык структурированных запросов . Это хрестоматийная демонстрация предложения group byс агрегатными функциями countи avg.

Bash предоставляет синтаксис +=, позволяющий легко это сделать.

4
09.11.2020, 13:46
1 ответ

Вы должны выполнить целочисленное деление, чтобы получить значение параметра -lв split. Оболочка отлично подходит для целочисленных делений:

lines_number=$(wc -l < file)
split -l $((lines_number / 10)) file
wc -l x*
  907 xaa
  907 xab
  907 xac
  907 xad
  907 xae
  907 xaf
  907 xag
  907 xah
  907 xai
  907 xaj
    4 xak
 9074 total

Если вы хотите использовать для этого awk, вы должны напечатать целое число:

wc -l file | awk '{print int($1/10)}'
907

И вам придется соединить последние два файла. Предполагая, что вы выводите их все в один и тот же пустой каталог, вы можете сделать:

printf "%s\n" x* | tail -n2 | xargs cat > last_file

wc -l < last_file
911

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


Примечание :Также я предпочитаю использовать собственный префикс и числовую индексацию для выходных файлов split, например:

split -d -l 907 file new_file

Примечание. :Поскольку длина суффикса по умолчанию равна 2, (см. man splitи-a)splitбудут называть файлы как new_file00, new_file01, которые снова сортируются в алфавитном порядке, если они меньше 100 (Длина суффикса из 3 цифр ). Другим вариантом одновременного использования цифровых суффиксов (для удобочитаемости )и алфавитного порядка является установка длины суффикса -aна соответствующее значение.


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

#!/bin/bash
f="file"
prefix="new_file"

lines_number=$(wc -l < "$f")
split -d -l $((lines_number / 10)) "$f" "$prefix"

if ((lines_number % 10 != 0)); then
    last_file=$(printf "%s\n" "$prefix"* | tail -1)
    pre_last_file=$(printf "%s\n" "$prefix"* | tail -2 | head -1)
    cat "$last_file" >> "$pre_last_file" && rm -- "$last_file"
fi
9
18.03.2021, 22:51

Теги

Похожие вопросы