Как использовать CSV-файл в качестве входных данных для основных арифметических операций в bash

#bash #csv #math

#bash #csv #математика

Вопрос:

Я сохранил свои данные в neckrev_dim.csv файле, структурированном следующим образом

 subjectID,dim3,pixdim3 
MR44825,405,0.625
 

У меня также есть отдельный subjects.csv , содержащий только все идентификаторы объектов

 MR44825
MR55843
 

Теперь я хочу использовать эти данные в основных арифметических операциях с использованием bash.

 subjlist=subjects.csv
for subj in ` cat $subjlist `
do
    dim3=$(grep -w '$subj' neckrev_dim.csv | cut -d ',' -f 2)
    pixdim3=$(grep -w '$subj' neckrev_dim.csv | cut -d ',' -f 3)
    total_length=$(($dim3*$pixdim3))
    echo $total_length
done
 

Это приводит к следующей ошибке:

 syntax error: operand expected (error token is "*")
 

Я думаю, что проблема заключается в grep , но я не могу понять это.

Заранее спасибо!

Ответ №1:

Основная проблема заключается в том, что арифметика POSIX не поддерживает десятичные дроби, только целые числа.

Вам придется использовать что-то другое, например bc , для нецелочисленной арифметики.

Другая проблема заключается в том, что вы заключаете в одинарные кавычки $subj — вы должны использовать двойные кавычки, чтобы переменная расширялась.

Попробуйте выполнить следующее:

 subjlist=subjects.csv

while read -r subj
do
    dim3=$(grep -w "$subj" neckrev_dim.csv | cut -d ',' -f 2)
    pixdim3=$(grep -w "$subj" neckrev_dim.csv | cut -d ',' -f 3)
    echo "$dim3 * $pixdim3" | bc
done < "$subjlist"
 

Обратите внимание, здесь bc выполняется чтение из стандартного ввода, поэтому нам просто нужно echo арифметическое выражение to bc .

Комментарии:

1. Работает так же отлично! Спасибо за решение и объяснение того, где я допускаю ошибки.

Ответ №2:

Вам нужно заменить одинарные кавычки на двойные кавычки вокруг $subj . Одинарные кавычки не будут расширять переменную.

Комментарии:

1. Ах, отлично, большое спасибо! Это уже решило мою первую проблему. Теперь единственная проблема в том, что я сохранил pixdim3 как десятичное число, используя . , поэтому он выдает мне еще одну ошибку 497*0.400024: syntax error: invalid arithmetic operator (error token is ".400024") . Знаете ли вы простое решение для этого?

2. Bash не может обрабатывать плавающую точку… попробуйте total=$(dc -e "$dim3 $pixdim3 * p") .

Ответ №3:

Решение, приведенное ниже, разработано для точной и более общей работы с различными типами значений ключей и различными строками CSV, избегая некоторых ограничений и режимов сбоев других решений.

Описание кода

Используя поля с одним ключом, считайте по одному на строку из файла keys.txt , найдите ключ в первом поле CSV-файла generic.csv и выполните некоторые вычисления с плавающей запятой (нецелые числа) для чисел в других полях.

Повышение производительности:

  1. Если $key выбрана уникальная строка в файле, измените XexitX ниже на exit , чтобы awk не продолжать чтение остальной части файла без необходимости; в противном случае удалите XexitX , и он выполнит все строки, соответствующие этому ключу.
  2. Если generic.csv это большой файл, отсортируйте его и замените awk строку на look --binary строку. Это заменит линейный поиск бинарным поиском. Убедитесь, что вы отсортировали весь файл:
    sort -o generic.csv generic.csv

Ограничения:

  1. $key Ключ не должен содержать обратную косую черту или двойные кавычки в awk версии. Это можно исправить с помощью sed -e 's/\/amp;amp;/g' -e 's/"/\"/g' on the field . look --binary Версия не имеет значения.
  2. В generic.csv файле должны использоваться только запятые, никаких CSV-полей «в кавычках». Это означает, что поля не должны содержать запятых.
  3. look --binary Версия сопоставляет префикс ключа в строках CSV, поэтому у вас не может быть ключа, который является префиксом другого, например, ключей ABC и AB не различается. В awk версии этой проблемы нет.

Преимущества этого по сравнению с другими решениями:

  1. Считывает CSV только один раз для каждого ключа, а не несколько раз.
  2. Совпадение $key точно по первому полю, а не по любым полям, которые могут быть добавлены к остальной части строки CSV — никаких ложных совпадений. ( look --binary Версия выполняет сопоставление префиксов, поэтому у вас не может быть ключа, который является префиксом другого.)
  3. Ключевое поле — это текстовое поле, а не регулярное выражение, поэтому оно может содержать специальные символы без необходимости беспокоиться об экранировании метасимволов регулярных выражений во избежание ошибок.
  4. Нет необходимости использовать grep или cut разделять поля; только один канал, а не три.
  5. Можно легко масштабировать до огромных файлов CSV, используя look --binary вместо awk .
 while read -r key ; do
    # SEE NOTES: look --binary "$key" generic.csv 
    awk -F, "$1 == "$key" { print ; XexitX }" generic.csv 
        | while IFS=, read -r key num1 num2  ; do
            echo "$key: $(dc -e "$num1 $num2 * p")"
        done
done <keys.txt