Bash скрипт для сортировки строк в CSV-файле - PullRequest
0 голосов
/ 27 января 2020

У меня есть несколько строк, и каждое поле в строке имеет заголовок, идентифицирующий это поле. В настоящее время файл представляет собой просто CSV-файл, и хотя первые несколько полей будут выстроены в строку, если поместить их в Excel, остальная часть строки будет выровнена из-за того, что в некоторых строках нет некоторых полей или поля вышли из строя. Я пытаюсь сделать так, чтобы каждое поле было выровнено с правильным заголовком столбца при копировании в Excel и с использованием инструмента «текст в столбцы». Я уверен, что это будет означать места заполнения в строках с соответствующим количеством запятых, чтобы обеспечить наличие достаточного количества пустых ячеек, чтобы выровнять это поле данных с правильным столбцом.

Input:
id1,id2,id3,id4,id5,id6,id7,id8
id1 field1,id2 field2,id3 field3,id8 field8,id5 field5,id6 field6,id7 field7,id4 field4
id1 field1,id6 field6,id3 field3,id4 field4,id5 field5,id2 field2,id8 field8
id1 field1,id4 field4,id7 field7,id6 field6,id5 field5,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id4 field4,id2 field2,id5 field5,id6 field6,id8 field8
id1 field1,id2 field2,id8 field8,id4 field4,id5 field5,id6 field6,id7 field7,id3 field3

Output:
id1,id2,id3,id4,id5,id6,id7,id8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,,id8 field8
id1 field1,,,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,,id4 field4,id5 field5,id6 field6,,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,,id7 field7,id8 field8

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

Я ничего не могу найти в Google, и я не уверен, как это сделать. Извините, больше не могу указывать c.


Новый набор данных запускается с помощью awk:

Input: 
id1,id2,id3,id4
id1.100 "field1",id2.100 "field2",id3.100 "field3",id4.100 "field4"
id1.101 "field1",id2.101 "field2",id3.101 "field3",id4.101 "field4"
id1.102 "field1",id2.102 "field2",id3.102 "field3",id4.102 "field4"
id1.103 "field1",id2.103 "field2",id3.103 "field3",id4.103 "field4"

output:
id1,id2,id3,id4
,,,
,,,
,,,
,,,

Не уверен, почему он это делает. Новый набор данных содержит символы "/": "" ("внутри кавычек в каждом поле. Число после символа". "В части id изменяется между каждым набором данных, который я бы использовал в этом сценарии sh.

Я просто попробовал это:

Input: 
id1.100,id2.100,id3.100,id4.100
id1.100 "field1",id2.100 "field2",id3.100 "field3",id4.100 "field4"
id1.101 "field1",id2.101 "field2",id3.101 "field3",id4.101 "field4"
id1.102 "field1",id2.102 "field2",id3.102 "field3",id4.102 "field4"
id1.103 "field1",id2.103 "field2",id3.103 "field3",id4.103 "field4"

output:
id1,id2,id3,id4
id1.100 "field1",id2.100 "field2",id3.100 "field3",id4.100 "field4"
,,,
,,,
,,,

Так есть ли способ идентифицировать поле идентификатора только по началу? Например, если поле идентификатора было Name.105, чтобы идентифицировать его только по строка "name"?


Повторяющиеся поля в наборе данных:

Input: 
id1.100,id2.100,id3.100,id4.100
id1.100 "field1",id2.100 "field2",id3.100 "field3",id3.100 "field3",id2.100 "field2"
id1.101 "field1",id2.101 "field2",id2.101 "field2",id3.101 "field3",id3.101 "field3"
id1.102 "field1",id2.102 "field2",id3.102 "field3",id4.103 "field4",id1.102 "field1"

Output:
id1.100,id2.100,id3.100,id4.100
id1.100 "field1",id2.100 "field2",id3.100 "field3",
id1.101 "field1",id2.101 "field2",id3.101 "field3",
id1.102 "field1",id2.102 "field2",id3.102 "field3",id4.103 "field4"

1 Ответ

2 голосов
/ 28 января 2020

Предполагается:

  • Идентификаторы являются произвольными строками, и общая сортировка (например, порядок словаря) не будет работать.
  • Идентификатор и поле не содержат пробелов и разделены через пробел.

Тогда как насчет:

declare -A id2val               # associative array to store id and fields
while IFS=, read -ra f; do
    if ((nr++ == 0)); then      # header line
        ids=("${f[@]}")         # assign ids in order
        (IFS=,; echo "${ids[*]}")
                                # print the header
    else
        id2val=()               # empty the associative array
        for ((i=0; i<${#f[@]}; i++)); do
                                # process each field of the input line
            id=${f[i]% *}       # extract the substring before space
            val=${f[i]#* }      # extract the substring after space
            id2val[$id]="$val"  # associate field value with the id
        done
        for ((i=0; i<${#ids[@]}; i++)); do
                                # process in the "id" order
            id=${ids[i]}        # retrieve the id
            if [[ -n ${id2val[$id]} ]]; then
                                # found the field associated to the id
                list[i]="$id ${id2val[$id]}"
                                # then format the csv field as output
            else
                list[i]=""
            fi
        done
        (IFS=,; echo "${list[*]}")
                                # print the record as csv
    fi
done < input.csv

Вывод:

id1,id2,id3,id4,id5,id6,id7,id8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,,id8 field8
id1 field1,,,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8
id1 field1,id2 field2,,id4 field4,id5 field5,id6 field6,,id8 field8
id1 field1,id2 field2,id3 field3,id4 field4,id5 field5,id6 field6,id7 field7,id8 field8

При обработке каждой записи сначала разбивает каждый столбец на пробеле в id и значение поля, затем сохраните их в ассоциативном массиве. Затем он проходит по идентификаторам в определенном заголовком порядке; если значение поля, связанное с идентификатором, найдено, то заполните fileld для вывода.

Без ассоциативного массива нам потребуется создать двойной l oop для соответствия идентификаторам, что будет неэффективно.

Если вы выбрали awk, вы также можете сказать:

awk 'BEGIN {FS=OFS=","}
    NR==1 {
        for (i=1; i<=NF; i++) ids[i] = $i
        nf = NF
        print
        next
    }
    {
        delete id2val
        for (i=1; i<=NF; i++) {
            split($i, a, " ")
            id2val[a[1]] = a[2]
        }
        for (i=1; i<=nf; i++) {
            id = ids[i]
            $i = (id2val[id] != "") ? id " " id2val[id] : ""
        }
        print
    }
' input.csv

, что будет более эффективным, чем решение bash.

[ОБНОВЛЕНИЕ]
Изменено для соответствия новому набору данных, предоставленному OP.

Новые данные не выполняются, поскольку оригинальный сценарий awk ожидает идентификаторы в строке заголовка и идентификаторы, оставленные пробелом и «полем», имеют одинаковый формат, не рассматривая точку как особое значение.
Не могли бы вы попробовать следующее:

awk 'BEGIN {FS=OFS=","}
    NR==1 {
        print
        for (i=1; i<=NF; i++) {
            sub("\\..*", "", $i) # remove the suffix, if any
            ids[i] = $i
        }
        nf = NF
        next
    }
    {
        delete id2val
        for (i=1; i<=NF; i++) {
            split($i, a, " ")
            split(a[1], b, ".") # splits the id on "." if any
            id2id[b[1]] = a[1]  # maps "id1" to "id1.100" e.g.
            id2val[b[1]] = a[2] # maps "id1" to "field1" e.g.
        }
        for (i=1; i<=nf; i++) {
            id = ids[i]
            $i = (id2val[id] != "") ? id2id[id] " " id2val[id] : ""
        }
        NF = nf                 # adjust the NF to "print" properly
        print
    }
' input.csv

Я изменил сценарий awk, чтобы разделить идентификаторы на точке, и ввел переменную id2id, чтобы получить исходную (включая точку и цифры) строку идентификатора.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...