#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
и выполните некоторые вычисления с плавающей запятой (нецелые числа) для чисел в других полях.
Повышение производительности:
- Если
$key
выбрана уникальная строка в файле, изменитеXexitX
ниже наexit
, чтобыawk
не продолжать чтение остальной части файла без необходимости; в противном случае удалитеXexitX
, и он выполнит все строки, соответствующие этому ключу. - Если
generic.csv
это большой файл, отсортируйте его и заменитеawk
строку наlook --binary
строку. Это заменит линейный поиск бинарным поиском. Убедитесь, что вы отсортировали весь файл:
sort -o generic.csv generic.csv
Ограничения:
$key
Ключ не должен содержать обратную косую черту или двойные кавычки вawk
версии. Это можно исправить с помощьюsed -e 's/\/amp;amp;/g' -e 's/"/\"/g'
on the field .look --binary
Версия не имеет значения.- В
generic.csv
файле должны использоваться только запятые, никаких CSV-полей «в кавычках». Это означает, что поля не должны содержать запятых. look --binary
Версия сопоставляет префикс ключа в строках CSV, поэтому у вас не может быть ключа, который является префиксом другого, например, ключейABC
иAB
не различается. Вawk
версии этой проблемы нет.
Преимущества этого по сравнению с другими решениями:
- Считывает CSV только один раз для каждого ключа, а не несколько раз.
- Совпадение
$key
точно по первому полю, а не по любым полям, которые могут быть добавлены к остальной части строки CSV — никаких ложных совпадений. (look --binary
Версия выполняет сопоставление префиксов, поэтому у вас не может быть ключа, который является префиксом другого.) - Ключевое поле — это текстовое поле, а не регулярное выражение, поэтому оно может содержать специальные символы без необходимости беспокоиться об экранировании метасимволов регулярных выражений во избежание ошибок.
- Нет необходимости использовать
grep
илиcut
разделять поля; только один канал, а не три. - Можно легко масштабировать до огромных файлов 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