通过 awk 循环列表

通过 awk 循环列表

我有两个文件: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 解析器将是一个更好的主意。

相关内容