我有两个文件:data.csv 和 list.txt。这是它们的外观示例
数据.csv:
"John","red","4"
"Basketball","orange","2"
"The Mike","blue","94"
"Lizard","purple","3"
"Johnny","pink","32"
列表.txt:
Mike
John
purple
32
现在,我想弄清楚如何制作一个循环
awk -F "\"*,\"*" '/**LIST ITEM**/ {print $1}' data.csv > output.txt
其中该命令针对 list.txt 的每一行运行,替换 **LIST ITEM**。如何才能做到这一点?
我在 MacOSX 10.5.7 上通过终端运行此程序。
编辑:
上述示例的期望输出是
The Mike
John
Johnny
Lizard
Johnny
编辑2:
更清楚地说,我试图避免这样做:
awk -F "\"*,\"*" '/Mike/ {print $1}' data.csv
awk -F "\"*,\"*" '/John/ {print $1}' data.csv
awk -F "\"*,\"*" '/purple/ {print $1}' data.csv
awk -F "\"*,\"*" '/32/ {print $1}' data.csv
相反,在一个命令中运行它,以某种方式循环遍历 list.txt 的所有行。
答案1
这符合您所需的输出顺序:
$ awk -F, '
NR == FNR {field1[$0] = $1; next}
{
for (line in field1)
if (line ~ $0)
print field1[line]
}
' data.csv list.txt
"The Mike"
"John"
"Johnny"
"Lizard"
"Johnny"
这会将 data.csv 文件读取到内存中,将整行映射到 field1。然后,根据 field1 数组的每个元素检查 list.txt 文件的每一行。
如果数据文件比列表文件大得多,那么将较小的文件保存在内存中并一次循环遍历较大的文件会更有意义:
$ awk -F, '
NR == FNR {list[$1]; next}
{
for (item in list)
if ($0 ~ item)
print $1
}
' list.txt data.csv
"John"
"The Mike"
"Lizard"
"Johnny"
"Johnny"
答案2
#!/bin/bash
while read -r line; do
awk -F '^"|","|"$' '$0 ~ line{print $2}' line="$line" data.csv
done < list.txt
概念验证
$ while read -r line; do awk -F '^"|","|"$' '$0 ~ line{print $2}' line="$line" data.csv; done < list.txt
The Mike
John
Johnny
Lizard
Johnny
该字段分隔符处理嵌入的引号和/或逗号
答案3
我不完全清楚你想要做什么:替换项目清单什么?只是在任何地方寻找匹配项并输出第一个字段?另外,您的示例list.txt
似乎与行中的任何位置匹配,这可能会出现问题:如果list.txt
在某个点包含该行怎么办e
?这将匹配示例中除最后一行之外的所有内容data.csv
。
awk -F '^"?|"?,"?|"$?' 'BEGIN {
# read list.txt into an array
while (getline pat < "list.txt") {
pats[pat] = 1
}
close("list.txt")
}
{
# skip empty field before leading "
if ($1 == "") {
res = $2
} else {
res = $1
}
# scan record for patterns stored earlier,
# output the first real data field (res) if
# found
for (pat in pats) {
if ($0 ~ pat) {
print res
}
}
}' data.csv
这比想象的要复杂一些;您的字段分隔符不处理第一个字段上的可选前导引号或最后一个字段上的可选尾随引号。我的确实如此,但代价是如果它在那里,第一个字段将为空(之前的空字符串^"?
)。它也不会尝试处理嵌入的引号。如果您需要支持随机通用 CSV,专用的 CSV 解析器将是一个更好的主意。