问题的性质:
我有一个包含 10 列的 CSV 文件,其中 4 列指定疾病代码。假设这些是第 1 - 4 列。我有 2 个包含“包含”和“排除”代码的文本文件。
包含文件如下:一个包含n
个输入字符串的文件,每个字符串换行
例子:
123
12300
12301
124
12400
12401
1250
排除文件如下:一个包含m
个输入字符串的文件,每个字符串也换行。
例子:
456
457
458
459
CSV 文件的截断版本如下所示:
D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
890,001,456,0009,A2,B2,C2,D2,E2,F2
12301,456,00,145,A3,B3,C3,D3,E3,F3
567,1250,010,321,A4,B4,C4,D4,E4,F4
使用 AWK,我如何获取名为 inclusion
和 exclusion
的 2 个文件以及返回以下内容的 CSV 文件:
D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
567,1250,010,321,A4,B4,C4,D4,E4,F4
CSV 文件可以有数百万行,而inclusion
和exclusion
文件可以有几十行。这不是家庭作业,感谢您的帮助。
最佳答案
使用grep
$ head -n1 <file; grep -E "(^|,)($(tr '\n' '|' <inclusion))(,|$)" file | grep -Ev "(^|,)($(tr '\n' '|' <exclusion))(,|$)"
D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
567,1250,010,321,A4,B4,C4,D4,E4,F4
使用 awk
$ awk -v inc="(^|,)($(tr '\n' '|' <inclusion))(,|$)" -v exc="(^|,)($(tr '\n' '|' <exclusion))(,|$)" 'NR==1 || ($0 ~ inc && ! ($0 ~ exc))' file
D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
567,1250,010,321,A4,B4,C4,D4,E4,F4
工作原理
对于 grep 和 awk 解决方案,关键步骤是创建匹配包含文件或排除文件的正则表达式。因为比较短,所以我们以exclusion
为例。我们可以为它创建一个正则表达式,如下所示:
$ echo "(^|,)($(tr '\n' '|' <exclusion))(,|$)"
(^|,)(456|457|458|459|)(,|$)
inclusion
的正则表达式与此类似。一旦创建了包含和排除正则表达式,我们就可以将它们与 grep 或 awk 一起使用。如果使用 awk,我们使用条件:
NR==1 || ($0 ~ inc && ! ($0 ~ exc))
如果此条件为真,则 awk 执行其默认操作,即打印该行。如果 (1) 我们在第一行,NR==1
或如果 (2) 该行在正则表达式中匹配以包含 inc
,并且与排除的正则表达式不匹配,exc
。
替代 awk 解决方案
$ gawk -F, -v inc="$(<inclusion)" -v exc="$(<exclusion)" 'BEGIN{n=split(inc,x,"\n"); for (j=1;j<=n;j++)incl[x[j]]=1; n=split(exc,x,"\n"); for (j=1;j<=n;j++)excl[x[j]]=1;} NR==1{print;next} {p=0;for (j=1;j<=NF;j++) if ($j in incl)p=1; for (j=1;j<=NF;j++) if ($j in excl) p=0;} p' file
D1,D2,D3,D4,A,B,C,D,E,F
123,00,145,567,A1,B1,C1,D1,E1,F1
567,1250,010,321,A4,B4,C4,D4,E4,F4
写成多行的相同代码如下所示:
gawk -F, -v inc="$(<inclusion)" -v exc="$(<exclusion)" '
BEGIN{
n=split(inc,x,"\n")
for (j=1;j<=n;j++)incl[x[j]]=1
n=split(exc,x,"\n")
for (j=1;j<=n;j++)excl[x[j]]=1
}
NR==1{
print
next
}
{
p=0
for (j=1;j<=NF;j++) if ($j in incl) p=1
for (j=1;j<=NF;j++) if ($j in excl) p=0
}
p
' file
上面的代码用inclusion
和exclusion
数据创建数组incl
和excl
。 incl
中包含字段的任何行都被标记为打印 p=1
。但是,如果该行包含 excl
中的字段,则 p
设置为 false,p=0
。
关于bash - 使用 AWK 使用来自多个输入文件的字符串作为 CSV 文件中选择列的搜索条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31258634/