Объединение/объединение CSV-файлов, которые не используют все свои заголовки/столбцы

Используйте команду join:

join -t $'\t' -1 1 -2 2 -o 2.1,1.1,1.2,2.3 \
    user_list.txt \
    <(sed -E 's/^(loginName:)\s+(\S+)\s+(.*)$/\1\t\2\t\3/' test.txt)

joinсоединит две таблицы, используя столбец 1 в первом файле(-1 1)и столбец 2 во втором файле (-2 2). Он напечатает столбцы в порядке, указанном с помощью -o.

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

join -t $'\t' -1 1 -2 2 -o 2.1,1.1,1.2,2.3 \
    <(xlsx2csv -d tab test.xlsx | tail -n +2) \
    <(sed -E 's/^(loginName:)\s+(\S+)\s+(.*)$/\1\t\2\t\3/' test.txt)

В Debian xlsx2csvдоступен в виде пакета(apt install xlsx2csv). joinявляется частью пакета coreutils.

Редактировать:Внесены изменения для использования табуляции вместо запятой в качестве разделителя и списка пользователей в виде простого текста вместо XLSX.

1
02.08.2019, 07:08
3 ответа

Предполагая, что между строками данных нет пустых строк и используется GNU awk для сортировки _в:

$ cat tst.awk
BEGIN { FS=OFS="," }
FNR==1 {
    delete f
    for (i=1; i<=NF; i++) {
        f[$i] = i
        flds[$i]
    }
    numFiles++
    next
}
{
    for (tag in f) {
        val[numFiles,tag] = $(f[tag])
    }
}
END {
    PROCINFO["sorted_in"] = "@val_str_asc"
    sep = ""
    for (tag in flds) {
        printf "%s%s", sep, tag
        sep = OFS
    }
    print ""
    for (fileNr=1; fileNr<=numFiles; fileNr++) {
        sep = ""
        for (tag in flds) {
            printf "%s%s", sep, val[fileNr,tag]
            sep = OFS
        }
        print ""
    }
}

.

$ awk -f tst.awk file{1..3}
A,B,C,D,E,F
10,20,10,20,,5
,20,10,20,5,10
,,,10,30,20
1
27.01.2020, 23:17
BEGIN {
        OFS = FS = ","

        # Parse given column headers and remeber their order.

        # nf will be the number of fields we'd want in the output.
        nf = split(pick, header)
        for (i = 1; i <= nf; ++i)
                order[header[i]] = i

        # Output headers.
        print pick
}

FNR == 1 {
        # Parse column headers from input file.

        delete reorder

        for (i = 1; i <= NF; ++i)
                # If the current header is one that we'd like to pick...
                if ($i in order)
                        #... record what column it is located in.
                        reorder[order[$i]] = i

        next
}

{
        # Process data fields from input file.

        # We build a new output record, so explicitly split the current record
        # and save it in the field array, then empty the record and rebuild.
        split($0, field)
        $0 = ""

        for (i = 1; i <= nf; ++i)
                # If reorder[i] is zero, it's a column that is not available in the
                # current file.
                $i = (reorder[i] == 0 ? "" : field[reorder[i]])

        print
}

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

Примеры с данными, которые вы показываете в вопросе:

$ awk -v pick='A,B,C,D,E,F' -f script.awk file*.csv
A,B,C,D,E,F
10,20,10,20,,5
,20,10,20,5,10
,,,10,30,20
$ awk -v pick='F,B,A' -f script.awk file*.csv
F,B,A
5,20,10
10,20,
20,,
1
27.01.2020, 23:17

Если вы хотите попробовать альтернативный, очень чистый и простой инструмент (https://github.com/johnkerl/miller), начните с папки, в которой у вас есть входные CSV-файлы, с помощью этой команды

mlr --csv unsparsify *.csv >out.csv

у вас будет

A,B,C,D,F,E
10,20,10,20,5,
,20,10,20,10,5
,,,10,20,30

Если вы хотите, чтобы F был в конце, команда

mlr --csv unsparsify then reorder -e -f F *.csv

Если у вас много файлов, вы можете сделать это в два этапа:

mlr --icsv cat *.csv >tmp.txt
mlr --ocsv unsparsify tmp.txt >out.csv
7
27.01.2020, 23:17

Теги

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