Переместите определенный столбец из файла CSV впереди (избранный столбец по имени)

Рассмотрите эти данные:

#!/usr/bin/env bash
cat > example_file.txt <<EOL
group, value, price
1, 3.21, 3.21
1, 3.42, 4.11
1, 3.5, 1.22
2, 4.1, 9.2
2, 4.2, 2.11
EOL

Я хочу переместить столбец 'значения' впереди:

value, price, group
3.21, 3.21, 1
3.42, 4.11, 1
3.5, 1.22, 1
4.1, 9.2, 2
4.2, 2.11, 2

проблема: порядок столбца (или даже число столбца или название многих из тех столбцов - кроме 'значения', которое всегда является там) варьируется от файла до файла. Таким образом, я должен выбрать столбец значений по имени (не, заказывают).

Как я могу сделать это?

2
задан 21 January 2019 в 14:20

4 ответа

Если Вы не возражаете value дублируемый столбец, Вы могли сделать что-то вроде этого, с csvtool:

$ csvtool paste <(csvtool namedcol value example_file.txt) example_file.txt 
value,group,value,price
3.21,1,3.21,3.21
3.42,1,3.42,4.11
3.5,1,3.5,1.22
4.1,2,4.1,9.2
4.2,2,4.2,2.11

Однако насколько я знаю csvtool не переместит (или удалит), a namedcol.

Если Вы не можете найти специализированный инструмент CSV, который будет, можно прокрутить собственное использование языка общего назначения, такого как Awk или Perl. Идея состояла бы в том, чтобы искать поля первой строки для индекса столбца соответствия, затем части и поставить на карту поля в выбранном порядке.

Например, с помощью текста жемчуга:: модуль CSV и этот прием, Как получить индекс определенного элемента (значение) массива?

$ perl -MText::CSV -lpe '
  BEGIN{ $p = Text::CSV->new({ allow_whitespace => 1 }) };
  @f = $p->fields() if $p->parse($_);
  ($i) = grep { $f[$_] eq "value" } (0..$#f) if $. == 1; 
  $_ = join ", ", splice(@f, $i, 1),  @f
' example_file.txt
value, group, price
3.21, 1, 3.21
3.42, 1, 4.11
3.5, 1, 1.22
4.1, 2, 9.2
4.2, 2, 2.11
3
ответ дан 2 December 2019 в 01:35

С великим Miller (http://johnkerl.org/miller/doc) очень легко

mlr --csv reorder -f " value, price,group" input.csv

Вы имеете

 value, price,group
 3.21, 3.21,1
 3.42, 4.11,1
 3.5, 1.22,1
 4.1, 9.2,2
 4.2, 2.11,2

Пожалуйста, примите во внимание: Я отредактировал свою команду, приняв во внимание пробелы на полевое название вопроса CSV

3
ответ дан 2 December 2019 в 01:35

Мое предложение является следующим сценарием:

#!/bin/bash

# Set a default value of the LABEL of the target column that must become first column
if [[ -z ${LABEL+x} ]]; then LABEL='value'; fi

# Process a single FILE
move_the_label_column_first() {
    # Read the LABELS on the first line of the input file as an array
    IFS=', ' read -a LABELS < <(cat "$FILE" 2>/dev/null | head -n1)

    # Find the number of the target column
    for ((COL = 0; COL < ${#LABELS[@]}; ++COL))
    do
        if [[ ${LABELS[$COL]} == "$LABEL" ]]
        then
            break
        fi
    done

    # Read each LINE from the input file as an array and output it in the new order
    while IFS=', ' read -a LINE
    do
        printf '%s, ' "${LINE[$COL]}" "${LINE[@]:0:$COL}" "${LINE[@]:$((COL + 1))}" | \
        sed 's/, $/\n/'
    done < <(cat "$FILE" 2>/dev/null)
}

# Process all input files, exclude the current script filename
for FILE in "$@"
do
    if [[ -f $FILE ]] && [[ $FILE != $(basename "$0") ]]
    then
        #echo "Input file: $FILE"
        move_the_label_column_first
    fi
done

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

$ cat in-file-1 
group, value, price
1, 3.21, 3.21
1, 3.42, 4.11
1, 3.5, 1.22

$ cat in-file-2
price, group, value, other
3.21, 1, 3.21, 7
4.11, 1, 3.42, 13
1.22, 1, 3.5, -1

Обработайте входной файл того:

$ ./reorder.sh in-file-1 
value, group, price
3.21, 1, 3.21
3.42, 1, 4.11
3.5, 1, 1.22

Обработайте два входных файла и измените маркировку столбца, который должен стать первым столбцом к price:

$ LABEL='price' ./reorder.sh in-file-1 in-file-2 
price, group, value
3.21, 1, 3.21
4.11, 1, 3.42
1.22, 1, 3.5
price, group, value, other
3.21, 1, 3.21, 7
4.11, 1, 3.42, 13
1.22, 1, 3.5, -1

Обработайте все файлы в каталоге:

$ ./reorder.sh *
value, group, price
3.21, 1, 3.21
3.42, 1, 4.11
3.5, 1, 1.22
value, price, group, other
3.21, 3.21, 1, 7
3.42, 4.11, 1, 13
3.5, 1.22, 1, -1

Процесс рекурсивно:

$ shopt -s globstar
$ ./reorder.sh **/*
value, group, price
3.21, 1, 3.21
...
1
ответ дан 2 December 2019 в 01:35

Используя csvtool , это можно сделать с помощью небольшой оболочки сценария оболочки:

# Make sure that the value column isn't in front already
if head -n 1 example_file.txt | grep -q '^value,'; then
    # Value already in front
    exit
fi

# Calculate the position of value in the header (1-indexed, as thats what csvtool use)
pos=$(($(head -n 1 example_file.txt | sed -e 's/, value\(,.*\|$\)//' | tr -d -c ',' | wc -c)+2))

# Move the value column as the pos-position to the front
csvtool col $pos,1-$((pos-1)),$((pos+1))- example_file.txt > example_file_fixed.txt

Обертка здесь определяет, в какой позиции находится столбец значений, в заголовке (считая запятые перед значением). Затем он использует команду csvtool , чтобы переставить столбцы в значение , <столбцы до>, <столбцы после> .

2
ответ дан 15 December 2019 в 23:17

Другие вопросы по тегам:

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