Вопрос или проблема
С помощью моего кода я пытаюсь суммировать значения с определённым именем столбца в CSV-файле в зависимости от вводимого имени. Вот мой код:
#!/bin/bash
updatedata() {
index=0
while IFS="" read -r line
do
IFS=';' read -ra array <<< "$line"
for arrpos in "${array[@]}"
do
if [ "$arrpos" == *"$1"* ] || [ "$1" == "$arrpos" ]
then
break
else
let index=index+1
fi
done
break
done < data.csv
((index=$index+1))
if [ $pos -eq 0 ]
then
v0=$(awk -F";" -v index=$index '{x+=$index}END{print x}' ./data.csv )
elif [ $pos -eq 1 ]
then
v1=$(awk -F";" '{x+=$index}END{print x}' ./data.csv )
elif [ $pos -eq 2 ]
then
v2=$(awk -F";" '{x+=$index}END{print x}' ./data.csv )
elif [ $pos -eq 3 ]
then
v3=$(awk -F";" '{x+=$index}END{print x}' ./data.csv )
fi
}
В середине кода вы можете видеть в v0=, я пытался немного поэкспериментировать, но продолжал получать ошибки:
Сначала я попробовал это:
v0=$(awk -F";" '{x+=$index}END{print x}' ./data.csv)
но это дало мне эту ошибку:
'awk: строка 1: синтаксическая ошибка в или близко к }'
поэтому я решил попробовать это (как вы можете видеть в коде)
v0=$(awk -F";" -v index=$index '{x+=$index}END{print x}' ./data.csv )
И я получил эту ошибку:
'awk: ошибка выполнения: нельзя присваивать значение командной строки индексу
конфликт типов или ключевое слово
FILENAME="" FNR=0 NR=0'
Я не знаю, что делать. Можете помочь мне.
Учитывая некоторые данные CSV в data.csv
,
A;B;C
1;2;3
4;5;6
-1.2;3;3.3
следующий скрипт рассчитает сумму столбца, названного переменной colname
, указанной в командной строке:
BEGIN {
FS = ";"
if (colname == "") {
print "Не удалось получить имя столбца (colname) для работы" >"/dev/stderr"
exit 1
}
}
FNR == 1 {
colnum = 0
for (i = 1; i <= NF; ++i)
if ($i == colname) {
colnum = i
break
}
if (colnum == 0) {
printf "Не удалось найти именованный столбец (colname = \"%s\")\n", colname >"/dev/stderr"
exit 1
}
sum = 0
next
}
{
sum += $colnum
}
END {
print sum
}
Тестирование:
$ awk -v colname="A" -f script.awk data.csv
3.8
$ awk -v colname="B" -f script.awk data.csv
10
$ awk -v colname="C" -f script.awk data.csv
12.3
$ awk -v colname="D" -f script.awk data.csv
Не удалось найти именованный столбец (colname = "D")
Сокращённый вариант скрипта без стольких проверок ошибок:
BEGIN { FS = ";" }
FNR == 1 {
for (i = 1; i <= NF; ++i)
if ($i == colname) break
if (i > NF) exit 1
next
}
{ sum += $i }
END { print sum }
или, как "однострочник":
$ awk -v colname="A" -F ';' 'FNR == 1 { for (i = 1; i <= NF; ++i) if ($i == colname) break; if (i > NF) exit 1; next } { sum += $i } END { print sum }' data.csv
Идеально, вы бы использовали какую-либо форму парсера CSV, например CSVkit:
$ csvstat --sum -c A data.csv
3.8
Утилита csvstat
вычисляет несколько различных статистик для любого данного CSV файла. Здесь она сама определяет, что разделителем является ;
. В этом примере я запрашиваю сумму столбца с именем A
.
Ответ или решение
Для того чтобы использовать переменные в команде awk
, вам необходимо учесть несколько важных аспектов синтаксиса и функциональности этого инструмента обработки текстов. Ваша задача заключается в суммировании значений в определённом столбце CSV файла, исходя из имени этого столбца, предоставленного пользователем. Давайте детально рассмотрим, как правильно применить переменные в вашем коде.
Исходный код
Ваш код имеет следующую структуру:
#!/bin/bash
updatedata() {
index=0
while IFS="" read -r line
do
IFS=';' read -ra array <<< "$line"
for arrpos in "${array[@]}"
do
if [ "$arrpos" == *"$1"* ] || [ "$1" == "$arrpos" ]
then
break
else
let index=index+1
fi
done
break
done < data.csv
((index=$index+1))
if [ $pos -eq 0 ]; then
v0=$(awk -F";" -v index=$index '{x+=$index}END{print x}' ./data.csv)
elif [ $pos -eq 1 ]; then
v1=$(awk -F";" '{x+=$index}END{print x}' ./data.csv)
fi
}
Ошибки в коде
-
Синтаксическая ошибка в
awk
:- Ваша строка
v0=$(awk -F";" '{x+=$index}END{print x}' ./data.csv)
вызывает ошибку, так как переменнаяindex
, определённая в Bash, не доступна в контекстеawk
. Вы должны использовать флаг-v
для передачи переменнойindex
вawk
.
- Ваша строка
-
Неправильное использование индексов в
awk
:- В
awk
индексы начинаются с 1, а не с 0, поэтому еслиindex
равен 1, вы должны обращаться к элементам массива с$index
.
- В
Исправленный код
Вот исправленный и более эффективный вариант вашего кода:
#!/bin/bash
updatedata() {
colname=$1
index=-1
# Определяем номер столбца
while IFS="" read -r line; do
IFS=';' read -ra array <<< "$line"
for arrpos in "${!array[@]}"; do
if [ "${array[arrpos]}" == "$colname" ]; then
index=$arrpos
break 2 # Выходим из обоих циклов
fi
done
done < data.csv
if [ $index -eq -1 ]; then
echo "Столбец с именем \"$colname\" не найден." >&2
return 1
fi
# Суммируем значения в определённом столбце
sum=$(awk -F";" -v index=$((index+1)) 'NR>1 {sum += $index} END {print sum}' data.csv)
echo "Сумма столбца \"$colname\": $sum"
}
Пояснение изменений
-
Переменная
colname
: Передаётся как параметр функции, упрощая ваш код и делая его более читабельным. -
Определение номера столбца: Используется более понятный и надежный способ для определения индекса нужного столбца.
-
Сумма в
awk
: Обратите внимание на использование-v index=$((index+1))
, так как индексация начинается с 1. Убедитесь, чтоNR>1
, чтобы пропустить заголовок.
Заключение
Использование переменных в awk
может быть непростым для понимания, но следуя данному примеру и рекомендациям, вы сможете писать более чистый и эффективный код для обработки CSV файлов. Если вам нужны более продвинутые решения для CSV данных, вы можете обратить внимание на специализированные инструменты, такие как csvkit
, которые значительно упростят работу с такими файлами.