Сортировка и разделение файла CSV с помощью sed или awk - PullRequest
1 голос
/ 12 мая 2019

У меня есть файл CSV (test.csv), который выглядит следующим образом:

WH_01,TRAINAMS,A10,1221-ESD
WH_03,TRAINLON,L10A3,3005-21
WH_01,TRAINAMS,A101,PWR-120
WH_02,TRAINCLE,A1,074-HD-SATA
WH_01,TRAINAMS,A10,PWR-120
WH_02,TRAINCLE,A15,102-55665
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,UK-B3,101859

1). Я могу отсортировать файл по значению в столбце № 2 следующим образом:

sort -t, -k2,2 test.csv > testsort.csv

2). Далее я хотел бы разбить файл на основе значения в столбце № 2. Используя приведенный выше пример, он должен создать 3 файла:

testsort_1.csv:
WH_01,TRAINAMS,A10,1221-ESD
WH_01,TRAINAMS,A101,PWR-120
WH_01,TRAINAMS,A10,PWR-120

testsort_2.csv:
WH_02,TRAINCLE,A1,074-HD-SATA
WH_02,TRAINCLE,A15,102-55665

testsort_3.csv:
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,L10A3,3005-21
WH_03,TRAINLON,UK-B3,101859

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

Спасибо.

Ответы [ 3 ]

2 голосов
/ 12 мая 2019

Хороший ход, разделяющий sort и awk.

$ sort -t, -k2,2 test.csv |awk -F, '!($2 in T) {T[$2]=++i} {print > ("testsort_" i ".csv")}'

$ tail -n +1 testsort*
==> testsort_1.csv <==
WH_01,TRAINAMS,A10,1221-ESD
WH_01,TRAINAMS,A101,PWR-120
WH_01,TRAINAMS,A10,PWR-120

==> testsort_2.csv <==
WH_02,TRAINCLE,A1,074-HD-SATA
WH_02,TRAINCLE,A15,102-55665

==> testsort_3.csv <==
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,L10A3,3005-21
WH_03,TRAINLON,UK-B3,101859
  • !($2 in T) - если второе поле не найдено в индексах массива T,
  • {T[$2]=++i} - увеличить счетчик и сохранить второе поле как индекс.
  • {print} - печатать каждую строку
  • > "file" - перезаписать, перенаправить и добавить вывод в файл
  • ("." i ".") - объединить "строки" и переменную
2 голосов
/ 12 мая 2019

Поскольку вы не уверены, что вам нужна сортировка, это почти наверняка означает, что вы этого не делаете, и вы просто думаете, что это будет полезно по какой-то причине, плюс вы просто сортируете по 2 доллара, а затем разбиваетесь на разные файлы на основезначение $ 2, так что сортировка не приносит никакой пользы.

Все, что вам действительно нужно сделать, это:

awk -F, '{print > ($2".csv")}'

Посмотрите:

$ ls
test.csv

$ awk -F, '{print > ($2".csv")}' test.csv

$ ls
test.csv  TRAINAMS.csv  TRAINCLE.csv  TRAINLON.csv

$ tail -n +1 TRAIN*
==> TRAINAMS.csv <==
WH_01,TRAINAMS,A10,1221-ESD
WH_01,TRAINAMS,A101,PWR-120
WH_01,TRAINAMS,A10,PWR-120

==> TRAINCLE.csv <==
WH_02,TRAINCLE,A1,074-HD-SATA
WH_02,TRAINCLE,A15,102-55665

==> TRAINLON.csv <==
WH_03,TRAINLON,L10A3,3005-21
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,UK-B3,101859

Если вы прошлиоколо 20 имен выходных файлов и не использовали GNU awk, тогда вам придется закрывать () каждый раз, когда изменяется $ 2, и использовать >> вместо > для добавления к ним.

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

awk -F, '!($2 in map){map[$2]="testsort_"++cnt".csv"} {print > map[$2]}' test.csv
0 голосов
/ 12 мая 2019

Вы можете сделать это довольно простым способом, сохранив счетчик имени файла и используя sprintf, чтобы создать имя файла для каждой последующей группы файлов.Вы используете FNR (номер записи файла), чтобы различать первую и последующие записи.

Например:

$ sort -t, -k2 file.csv | 
awk -F, -v cnt=1 -v fn="testsort_1.csv" '
    FNR==1 {
        prev=$2
        print $0 > fn
    } 
    FNR>1 {
        if ($2!=prev) {
            cnt++
            fn=sprintf("%s_%d.csv", "testsort", cnt)
        }
        print $0 > fn
        prev=$2
    }'

( примечание: вы установилиначальное имя файла в качестве переменной для начала, а затем создайте все последующие имена файлов из вашего cnt (количество) с помощью sprintf. prev отслеживает второе поле из предыдущей записи. fn - это имя файла, созданное sprintfи счетчик.)

Более короткая версия того же сценария, первоначально объявляющего prev переменной, будет:

sort -t, -k2 file.csv | 
awk -F, -v cnt=0 -v prev="" '{
    if ($2!=prev) {
        cnt++
        fn = "testsort_" cnt ".csv"
        prev=$2
    }
    print $0 > fn 
}'

Если вы не хотите иметь последовательно пронумерованные файлы,но вместо того, чтобы получить "testsort_number.csv", взятый из отсортированных записей, посмотрите на @ Cyrus теперь удаленный ответ, который обеспечивает превосходное (и более короткое) решение в этом отношении.(Я вижу, у вас уже есть отличный ответ)

Пример использования / Вывод

Если вы введете file.csv, будут созданы следующие выходные файлы:

$ for i in testsort_{1..3}.csv; do printf "\n%s\n" $i; cat $i; done

testsort_1.csv
WH_01,TRAINAMS,A10,1221-ESD
WH_01,TRAINAMS,A10,PWR-120
WH_01,TRAINAMS,A101,PWR-120

testsort_2.csv
WH_02,TRAINCLE,A1,074-HD-SATA
WH_02,TRAINCLE,A15,102-55665

testsort_3.csv
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,L10A3,3005-21
WH_03,TRAINLON,UK-B3,101859
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...