如何使用 Linux cmd 丢弃基于两个文件的公共列的行?
How to discard row based on a common column of two files using Linux cmd?
file1.csv
col1,col2,col3
a,b,c
b,v,n
x,u,v
t,m,m
file2.csv
col1,col2,col3
p,m,n
a,z,i
col1
在两个文件中充当主键。如果 file2.csv
的 col1
的任何值出现在 file1.csv
中,则此行将从 file1.csv
.
中丢弃
输出文件:
col1,col2,col3
b,v,n
x,u,v
t,m,m
注意:我对基于 Unix
的解决方案很感兴趣。请使用sort
、uniq
、join
等提供解决方案
在 Unix 上有一个 join
命令几乎完全符合您的要求:
join -v1 -t, \
<(tail +2 file1.txt | sort -k1 -t,) \
<(tail +2 file2.txt | sort -k1 -t,)
对于您提供的示例文件,这是其输出:
b,v,n
t,m,m
x,u,v
命令分解
join -v1 -t,
-v1
:显示第一个文件中无法通过连接列与第二个文件中的行配对的行(用于连接的默认列为 1,但可以通过 -1
覆盖和 -2
个选项)
-t,
: 使用逗号作为 field/column 分隔符
<(tail +2 file1.txt | sort -k1 -t,)
<( … )
:join
命令需要文件名作为参数,因此我们使用 process substitution 从嵌套命令的输出创建此类临时文件
tail +2 file1.txt
: 跳过header行
sort -k1 -t,
: join
命令需要排序的文件
-t,
: 使用逗号作为 field/column 分隔符
-k1
: 按第一个字段排序
使用 R 编程语言:
> needle <- read.csv("/path/to/file2.csv", stringsAsFactors=FALSE)
> needle
col1 col2 col3
1 p m n
2 a z i
>
> haystack <- read.csv("/path/to/file1.csv", stringsAsFactors=FALSE)
> haystack
col1 col2 col3
1 a b c
2 b v n
3 x u v
4 t m m
>
> haystack[!(haystack$col1 %in% needle$col1), ]
col1 col2 col3
2 b v n
3 x u v
4 t m m
>
上面的代码应该是不言自明的。对于 R 中的二维数据,索引是用逗号分隔的下标完成的。例如,对于数据框 csv_data[i,j]
,i
-indexing_value 表示行,而 j
-indexing_value 表示列。
[R
中的索引从数字 1
开始,而不是 0
。此外,根据 R 安装的年龄,将参数 stringsAsFactors=FALSE
包含到每个 read.csv()
函数调用中可能是多余的。
最后一行给出了所需的结果,您可以从字面上理解代码的意思是:"for the haystack dataframe find and return rows where haystack$col1
不包含(即不 %in%
)needle$col1
值"。
https://www.r-project.org/
https://cran.r-project.org/index.html
使用 Raku(以前称为 Perl_6)
~$ raku -e ' \
my @needle = "/path/to/file2.csv".IO.lines.skip(1).map: *.split(","); \
my @haystack = "/path/to/file1.csv".IO.lines.skip(1).map: *.split(","); \
my @x = ([Z] @needle).[0]; my @y = @haystack.map(*.[0,3...*]); \
.join(",").put for @haystack[@y.grep({!/@x/}, :k)]; ' file
示例输出:
b,v,n
x,u,v
t,m,m
以上是使用 Raku 编程语言的解决方案,Raku 编程语言是 Perl 编程语言家族的一员。
简而言之,文件被读取 line
-wise,skip
-ping 第一个 (header) 行,map
用于 split
“,
”(逗号)上的每一行。每个 csv
文件都存储为一个 @
-sigiled Raku 数组。
在 object 创建的两行代码之后,每个数组都被提炼成一列值。 @needle
数组被提炼为 @x
,而 @haystack
object 被提炼为 @y
。为了突出 Raku 的灵活性 (TIMTOWDI),显示了两种不同的提取所需列的方法:
@x
使用 [Z]
"zip" 运算符提取每行的元素
这样每行的第一个元素(即第一列)是
提取。
@y
使用数字索引序列 [0,3...*]
来提取
所需的元素。 Raku 足够聪明,可以找出剩下的
序列值 [0,3,6,9]
.
最后,Raku 的 grep
函数用于搜索 @x
和 @y
之间的匹配项。 :k
(键)“副词”参数告诉 Raku return 匹配的数字索引位置,或者在这种情况下--non-matches,因为 grep
使用 !{…}
布尔块否定 returned 匹配值。
ADDENDUM: 正如@EdMorton 在评论中指出的那样,上面的代码不输出列名(file1.csv
的第一行)。这很容易通过添加以下行来解决(最好将其设为行 before my @needle =
... .):
"/path/to/file1.csv".IO.lines[0].put;
在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ awk -F, '
FNR==1 { if (NR==1) print; next }
NR==FNR { a[]; next }
!( in a)
' file2.csv file1.csv
col1,col2,col3
b,v,n
x,u,v
t,m,m
file1.csv
col1,col2,col3
a,b,c
b,v,n
x,u,v
t,m,m
file2.csv
col1,col2,col3
p,m,n
a,z,i
col1
在两个文件中充当主键。如果 file2.csv
的 col1
的任何值出现在 file1.csv
中,则此行将从 file1.csv
.
输出文件:
col1,col2,col3
b,v,n
x,u,v
t,m,m
注意:我对基于 Unix
的解决方案很感兴趣。请使用sort
、uniq
、join
等提供解决方案
在 Unix 上有一个 join
命令几乎完全符合您的要求:
join -v1 -t, \
<(tail +2 file1.txt | sort -k1 -t,) \
<(tail +2 file2.txt | sort -k1 -t,)
对于您提供的示例文件,这是其输出:
b,v,n
t,m,m
x,u,v
命令分解
join -v1 -t,
-v1
:显示第一个文件中无法通过连接列与第二个文件中的行配对的行(用于连接的默认列为 1,但可以通过-1
覆盖和-2
个选项)-t,
: 使用逗号作为 field/column 分隔符
<(tail +2 file1.txt | sort -k1 -t,)
<( … )
:join
命令需要文件名作为参数,因此我们使用 process substitution 从嵌套命令的输出创建此类临时文件tail +2 file1.txt
: 跳过header行sort -k1 -t,
:join
命令需要排序的文件-t,
: 使用逗号作为 field/column 分隔符-k1
: 按第一个字段排序
使用 R 编程语言:
> needle <- read.csv("/path/to/file2.csv", stringsAsFactors=FALSE)
> needle
col1 col2 col3
1 p m n
2 a z i
>
> haystack <- read.csv("/path/to/file1.csv", stringsAsFactors=FALSE)
> haystack
col1 col2 col3
1 a b c
2 b v n
3 x u v
4 t m m
>
> haystack[!(haystack$col1 %in% needle$col1), ]
col1 col2 col3
2 b v n
3 x u v
4 t m m
>
上面的代码应该是不言自明的。对于 R 中的二维数据,索引是用逗号分隔的下标完成的。例如,对于数据框 csv_data[i,j]
,i
-indexing_value 表示行,而 j
-indexing_value 表示列。
[R
中的索引从数字 1
开始,而不是 0
。此外,根据 R 安装的年龄,将参数 stringsAsFactors=FALSE
包含到每个 read.csv()
函数调用中可能是多余的。
最后一行给出了所需的结果,您可以从字面上理解代码的意思是:"for the haystack dataframe find and return rows where haystack$col1
不包含(即不 %in%
)needle$col1
值"。
https://www.r-project.org/
https://cran.r-project.org/index.html
使用 Raku(以前称为 Perl_6)
~$ raku -e ' \
my @needle = "/path/to/file2.csv".IO.lines.skip(1).map: *.split(","); \
my @haystack = "/path/to/file1.csv".IO.lines.skip(1).map: *.split(","); \
my @x = ([Z] @needle).[0]; my @y = @haystack.map(*.[0,3...*]); \
.join(",").put for @haystack[@y.grep({!/@x/}, :k)]; ' file
示例输出:
b,v,n
x,u,v
t,m,m
以上是使用 Raku 编程语言的解决方案,Raku 编程语言是 Perl 编程语言家族的一员。
简而言之,文件被读取 line
-wise,skip
-ping 第一个 (header) 行,map
用于 split
“,
”(逗号)上的每一行。每个 csv
文件都存储为一个 @
-sigiled Raku 数组。
在 object 创建的两行代码之后,每个数组都被提炼成一列值。 @needle
数组被提炼为 @x
,而 @haystack
object 被提炼为 @y
。为了突出 Raku 的灵活性 (TIMTOWDI),显示了两种不同的提取所需列的方法:
@x
使用[Z]
"zip" 运算符提取每行的元素 这样每行的第一个元素(即第一列)是 提取。@y
使用数字索引序列[0,3...*]
来提取 所需的元素。 Raku 足够聪明,可以找出剩下的 序列值[0,3,6,9]
.
最后,Raku 的 grep
函数用于搜索 @x
和 @y
之间的匹配项。 :k
(键)“副词”参数告诉 Raku return 匹配的数字索引位置,或者在这种情况下--non-matches,因为 grep
使用 !{…}
布尔块否定 returned 匹配值。
ADDENDUM: 正如@EdMorton 在评论中指出的那样,上面的代码不输出列名(file1.csv
的第一行)。这很容易通过添加以下行来解决(最好将其设为行 before my @needle =
... .):
"/path/to/file1.csv".IO.lines[0].put;
在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ awk -F, '
FNR==1 { if (NR==1) print; next }
NR==FNR { a[]; next }
!( in a)
' file2.csv file1.csv
col1,col2,col3
b,v,n
x,u,v
t,m,m