Рассмотрите эти данные:
#!/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
проблема: порядок столбца (или даже число столбца или название многих из тех столбцов - кроме 'значения', которое всегда является там) варьируется от файла до файла. Таким образом, я должен выбрать столбец значений по имени (не, заказывают).
Как я могу сделать это?
Если Вы не возражаете 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
С великим 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
Мое предложение является следующим сценарием:
#!/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
...
Используя 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
, чтобы переставить столбцы в значение , <столбцы до>, <столбцы после>
.