从 CSV 文件中删除与来自 bash 的另一个文件中的行相匹配的行?
Remove lines from CSV file that matches lines from another file from bash?
我有一个具有以下结构的(大)CSV 文件 (A):
1234ABC 456789
1235ABD 098732
1235ABE 098731
1235ABF 198731
另一个文件 (B) 包含应从 A 中删除的条目:
1234ABC
1235ABE
我想 运行 一个 awk
或 sed
命令(或者一些命令行脚本,如果 awk
或 sed
不够)从 A 中删除所有行,其第一列等于 B 中的一行。即脚本具有 运行 后 A 中的结果应为:
1235ABD 098732
1235ABF 198731
请注意,仅删除 A 中 以 B 中任何行开头的行是不够的。例如,如果 A 包含:
1235AC 456789
1235A 098732
并且 B 包含:
1235A
那么 A 之后应该包含这个:
1235AC 456789
如何在 bash 中实现此目的,最好使用 awk
或 sed
(如果需要,也可以使用 shell 脚本)?
你可以使用这个 awk
:
awk 'NR == FNR {dels[]; next} !( in dels)' file2.csv file1.csv
1235ABD 098732
1235ABF 198731
这是标准的 2 遍 awk 命令,它将第一遍 file2
的所有行存储在数组 dels
.
中
在第二遍中,我们只打印来自 file1
的行,其中 </code> 不存在于数组 <code>dels
.
中
$ cat fileA
1234ABC 456789
1235ABD 098732
1235ABE 098731
1235ABF 198731
1235AC 456789
1235A 098732
$ cat fileB
1234ABC
1235ABE
1235A
一个 grep
使用来自文件 fileB
:
的反向单词匹配的想法
$ grep -vwf fileB fileA
1235ABD 098732
1235ABF 198731
1235AC 456789
注意: 这会将匹配应用于整行(即,不仅仅是第一列),因此如果来自 [=13= 的条目可能不准确] 可以出现在 fileA
的第 2 至 N 列中
使用这个 Perl 单行代码:
perl -lane 'BEGIN { %exclude = map { chomp; { $_ => 1 } } `cat B`; } print if ! $exclude{ $F[0] };' A
打印:
1235ABD 098732
1235ABF 198731
Perl 单行代码使用这些命令行标志:
-e
: 告诉 Perl 查找内联代码,而不是在文件中。
-n
:一次循环输入一行,默认分配给 $_
。
-l
: 在执行内联代码之前去除输入行分隔符(默认情况下在 *NIX 上为 "\n"
),并在打印时附加它。
-a
: 在空白处将 $_
拆分为数组 @F
。
首先执行BEGIN {...}
块。文件 B
的内容存储在散列 %exclude
中,键 = 文件 B
的行,值 = 1。chomp
删除终端换行符。注意B
存储在内存中,对于大文件和小RAM可能是个问题。
-lane
:使脚本逐行遍历文件 A
,将以空格分隔的字段存储在数组 @F
中。那么,$F[0]
就是A
.
中每一行的第一列
print if ! $exclude{ $F[0] };
:如果在 B
中找不到第一个字段 ($F[0]
)(= 不在散列 %exclude
中),则打印 A
行。
另请参见:
perldoc perlrun
: how to execute the Perl interpreter: command line switches
我有一个具有以下结构的(大)CSV 文件 (A):
1234ABC 456789
1235ABD 098732
1235ABE 098731
1235ABF 198731
另一个文件 (B) 包含应从 A 中删除的条目:
1234ABC
1235ABE
我想 运行 一个 awk
或 sed
命令(或者一些命令行脚本,如果 awk
或 sed
不够)从 A 中删除所有行,其第一列等于 B 中的一行。即脚本具有 运行 后 A 中的结果应为:
1235ABD 098732
1235ABF 198731
请注意,仅删除 A 中 以 B 中任何行开头的行是不够的。例如,如果 A 包含:
1235AC 456789
1235A 098732
并且 B 包含:
1235A
那么 A 之后应该包含这个:
1235AC 456789
如何在 bash 中实现此目的,最好使用 awk
或 sed
(如果需要,也可以使用 shell 脚本)?
你可以使用这个 awk
:
awk 'NR == FNR {dels[]; next} !( in dels)' file2.csv file1.csv
1235ABD 098732
1235ABF 198731
这是标准的 2 遍 awk 命令,它将第一遍 file2
的所有行存储在数组 dels
.
在第二遍中,我们只打印来自 file1
的行,其中 </code> 不存在于数组 <code>dels
.
$ cat fileA
1234ABC 456789
1235ABD 098732
1235ABE 098731
1235ABF 198731
1235AC 456789
1235A 098732
$ cat fileB
1234ABC
1235ABE
1235A
一个 grep
使用来自文件 fileB
:
$ grep -vwf fileB fileA
1235ABD 098732
1235ABF 198731
1235AC 456789
注意: 这会将匹配应用于整行(即,不仅仅是第一列),因此如果来自 [=13= 的条目可能不准确] 可以出现在 fileA
使用这个 Perl 单行代码:
perl -lane 'BEGIN { %exclude = map { chomp; { $_ => 1 } } `cat B`; } print if ! $exclude{ $F[0] };' A
打印:
1235ABD 098732
1235ABF 198731
Perl 单行代码使用这些命令行标志:
-e
: 告诉 Perl 查找内联代码,而不是在文件中。
-n
:一次循环输入一行,默认分配给 $_
。
-l
: 在执行内联代码之前去除输入行分隔符(默认情况下在 *NIX 上为 "\n"
),并在打印时附加它。
-a
: 在空白处将 $_
拆分为数组 @F
。
首先执行BEGIN {...}
块。文件 B
的内容存储在散列 %exclude
中,键 = 文件 B
的行,值 = 1。chomp
删除终端换行符。注意B
存储在内存中,对于大文件和小RAM可能是个问题。
-lane
:使脚本逐行遍历文件 A
,将以空格分隔的字段存储在数组 @F
中。那么,$F[0]
就是A
.
中每一行的第一列
print if ! $exclude{ $F[0] };
:如果在 B
中找不到第一个字段 ($F[0]
)(= 不在散列 %exclude
中),则打印 A
行。
另请参见:
perldoc perlrun
: how to execute the Perl interpreter: command line switches