我有两个 .csv 文件。第一个文件有一列单词。第二个文件有两列,第一列包含与第一个文件中的一个条目匹配的值。我想逐行读取第一个文件,并使用每一行对第二个文件进行 grep 查询。显示当前代码
- 读取行时
- 做
- grep $line ./filetwo.csv
- 完成 < fileone.csv
这段代码什么也没产生。如果我用一个不是通过读取文件分配的变量替换 $line ,它就可以完美工作。我多年来一直在研究这个问题,但从未找到一个看似简单的问题的答案。我不明白为什么通过读取 .csv 文件分配的变量不能提供与直接分配的变量相同的结果。我正在使用 zsh shell。
答案1
CSV 文件在 Microsoft 世界中更为常见,因此您可能会发现:
- 它们以 UTF-16 编码,而不是区域设置的字符集,因此需要进行转换。
- 或者它们以 UTF-8 编码但带有字节顺序标记。
- 他们有 CRLF 行分隔符。
- 它们的最后一行没有分隔(因此
read
会返回 false)。
您可以检查一下是否属于这种情况file yourfile.csv
。
然后你可以这样做:
dos2unix < fileone.csv |
while IFS=, read -r first rest_if_any_ignored; do
dos2unix < filetwo.csv | grep -Fe "$first"
done
(请注意-F
for 固定字符串搜索,而不是默认的进行正则表达式匹配( in re
)grep
),但这效率相当低,因为它为每一行运行三个命令fileone.csv
,并且每个命令每次都从头开始grep
处理内容。filetwo.csv
$first
它还会在 中的任何位置查找字符串filetwo.csv
,而不仅仅是第一列,并且不会执行精确匹配。例如,如果$first
is foo
,则将报告foobar,other
和other,foobar
行。这也不处理 CSV 引用。因此,您最好使用具有正确 CSV 解析功能的语言。
如果这些文件是简单的 CSV,即没有引用也没有标题,那么这将是这里的工作join
:
preprocess() {
dos2unix -O -- "$@" | sort -t, -k1b,1
}
join -t, <(preprocess < fileone.csv) <(preprocess < filetwo.csv)
对于真正的 CSV,带有标题和可能的引用(包括包含换行符的数据),您可以使用 CSV 解析器,mlr
例如它的join
动词。
例如,如果第一列被称为foo
infileone.csv
和bar
in filetwo.csv
:
mlr --csv join -j foo -r bar -f fileone.csv filetwo.csv
它可以处理 CRLF、无分隔行和带 BOM 的 UTF-8,但不能处理 UTF-16,您需要先使用 或 转换为 UTF- dos2unix
8 iconv
。
mlr
还可以执行简单的 CSV 和其他几种表格格式。详细信息请查看其手册。