Как я могу использовать переменную в команде 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 )
    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
}

Ошибки в коде

  1. Синтаксическая ошибка в awk:

    • Ваша строка v0=$(awk -F";" '{x+=$index}END{print x}' ./data.csv) вызывает ошибку, так как переменная index, определённая в Bash, не доступна в контексте awk. Вы должны использовать флаг -v для передачи переменной index в awk.
  2. Неправильное использование индексов в 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"
}

Пояснение изменений

  1. Переменная colname: Передаётся как параметр функции, упрощая ваш код и делая его более читабельным.

  2. Определение номера столбца: Используется более понятный и надежный способ для определения индекса нужного столбца.

  3. Сумма в awk: Обратите внимание на использование -v index=$((index+1)), так как индексация начинается с 1. Убедитесь, что NR>1, чтобы пропустить заголовок.

Заключение

Использование переменных в awk может быть непростым для понимания, но следуя данному примеру и рекомендациям, вы сможете писать более чистый и эффективный код для обработки CSV файлов. Если вам нужны более продвинутые решения для CSV данных, вы можете обратить внимание на специализированные инструменты, такие как csvkit, которые значительно упростят работу с такими файлами.

Оцените материал
Добавить комментарий

Капча загружается...