【问题标题】:How to use a csv file as input for basic arithmetic operations in bash如何使用 csv 文件作为 bash 中基本算术运算的输入
【发布时间】:2020-12-29 12:44:56
【问题描述】:

我已将数据存储在neckrev_dim.csv 文件中,结构如下

subjectID,dim3,pixdim3 
MR44825,405,0.625

我还有一个单独的subjects.csv,只包含所有的subjectIDs

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,但我想不通。

提前致谢!

【问题讨论】:

    标签: bash csv math


    【解决方案1】:

    以下解决方案旨在针对不同类型的键值和不同的 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/\\/&&/g' -e 's/"/\\"/g' 修复。 look --binary 版本不在乎。
    2. generic.csv 文件只能使用逗号,不能使用“引用”的 CSV 字段。这意味着任何字段都不能包含逗号。
    3. look --binary 版本在 CSV 行上进行键前缀匹配,因此您不能拥有一个键是另一个前缀的键,例如键 ABCAB 没有区别。 awk 版本没有这个问题。

    与其他解决方案相比的优势:

    1. 每个键仅读取 CSV 一次,而不是多次。
    2. $key 在第一个字段上完全匹配,而不是在可能添加到 CSV 行其余部分的任何字段上 - 没有错误匹配。 (look --binary 版本确实会进行前缀匹配,因此您不能拥有一个作为另一个前缀的键。)
    3. 关键字段是文本字段,不是正则表达式,因此它可能包含特殊字符,无需担心转义正则表达式元字符以避免错误。
    4. 无需使用grepcut分隔字段;只有一根管子,不是三根。
    5. 可以通过使用look --binary 而不是awk 轻松扩展到大型CSV 文件。
    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
    

    【讨论】:

      【解决方案2】:

      主要问题是 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的算术表达式转为bc即可。

      【讨论】:

      • 效果也很好!感谢您的解决方案以及我在哪里犯错的解释。
      【解决方案3】:

      您需要将$subj 周围的单引号更改为双引号。单引号不会扩展变量。

      【讨论】:

      • 啊,太好了,非常感谢!这已经解决了我的第一个问题。现在唯一的问题是,我使用.pixdim3 存储为十进制,所以它给我带来了另一个错误497*0.400024: syntax error: invalid arithmetic operator (error token is ".400024")。你知道一个简单的解决方法吗?
      • Bash 无法处理浮点...试试total=$(dc -e "$dim3 $pixdim3 * p")
      猜你喜欢
      • 2015-10-15
      • 2019-07-21
      • 1970-01-01
      • 2012-12-17
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      • 1970-01-01
      • 2021-12-14
      相关资源
      最近更新 更多