Фильтрация данных CSV в несколько отдельных файлов

Вдохновленный другим ответом, вот более простой контекстный обработчик ошибок:

trap '>&2 echo Command failed: $(tail -n+$LINENO $0 | head -n1)' ERR

Вы также можете использовать awk вместо tail & head , если необходимо.

1
19.05.2020, 00:51
1 ответ

Предполагая, что данные являются простыми данными CSV, т. е. ни одно поле не содержит встроенных разделителей или новых строк:

awk -F ';' '
    {
        print > "file" $2    ".csv"
        print > "file" $2 $6 ".csv"
    }' file1.csv

Это печатает каждую строку дважды, один раз в файл, заданный только значением второго поля, и один раз в файл, заданный комбинацией значений второго и шестого полей. Каждое имя выходного файла будет иметь префикс строки fileи суффикс строки .csvв соответствии с текстом вопроса.

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

Если вы хотите включить название страны из четвертого поля:

awk -F ';' '
    {
        print > "file_" $2 "-" $4        ".csv"
        print > "file_" $2 "-" $4 "_" $6 ".csv"
    }' file1.csv

Для заданных данных будут созданы следующие файлы

file_AD-Andorra.csv
file_AD-Andorra_type_1.csv
file_AD-Andorra_type_2.csv
file_NL-Netherlands.csv
file_NL-Netherlands_type_2.csv
file_US-United States.csv
file_US-United States_type_2.csv

Вышеописанное будет хорошо работать в системе, использующей GNU awk. Другие реализации awkмогут столкнуться с проблемами, связанными с одновременным сохранением слишком большого количества файлов открытыми для записи. В таких реализациях awkвам придется быть умнее и не забывать закрывать файлы после записи в них. Когда файл закрыт, нужно не забыть напечатать с >>в следующий раз, когда данные должны быть записаны в файл, иначе файл будет усечен.

awk -F ';' '
    function do_print(name) {
        if (seen[name] == 1) print >>name  # append to file
        else                 print  >name  # first write, truncate file
        close(name)
        seen[name] = 1
    }
    {
        do_print("file_" $2 "-" $4        ".csv")
        do_print("file_" $2 "-" $4 "_" $6 ".csv")
    }' file1.csv

Это также заставит код работать с awkв OpenBSD, с которым вы не можете print >в выражение.


Дополнительно (просто для удовольствия ):Заставить код awkвыводить некоторую статистику:

awk -F ';' '
    function do_print(name) {
        if (seen[name] > 0) print >>name  # append to file
        else                print  >name  # first write, truncate file
        close(name)
        seen[name]++
    }
    {
        do_print("file_" $2 "-" $4        ".csv")
        do_print("file_" $2 "-" $4 "_" $6 ".csv")
    }
    END {
        for (name in seen)
            printf "Wrote %d lines to \"%s\"\n", seen[name], name >"/dev/stderr"
    }' file1.csv

Это записывает некоторую статистику в поток ошибок в конце обработки. Для приведенных данных:

Wrote 1 lines to "file_NL-Netherlands.csv"
Wrote 1 lines to "file_US-United States_type_2.csv"
Wrote 1 lines to "file_AD-Andorra_type_1.csv"
Wrote 2 lines to "file_AD-Andorra.csv"
Wrote 1 lines to "file_NL-Netherlands_type_2.csv"
Wrote 1 lines to "file_US-United States.csv"
Wrote 1 lines to "file_AD-Andorra_type_2.csv"
2
28.04.2021, 23:13

Теги

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