AWK:按第一列匹配两个文件不起作用

AWK : matching two files by first column doesn't work

提问人:Dexter0015 提问时间:8/22/2023 最后编辑:Dexter0015 更新时间:8/31/2023 访问量:128

问:

我使用 awk 几乎没有问题,它开始给我白发..

我有两个内容不同的文件,但第一列值是相同的。

文件在脚本期间以 csv 文件(分号分隔)和 lokks 的形式生成,如下所示:

main_file.csv
---
151597-21;151597;21;3;15;;"Vente OK";excluded
151598-21;151598;21;3;15;;"Vente OK";excluded
151599-0;151599;0;0;10;;;programmed
151600-0;151600;0;0;10;;;programmed
151601-0;151601;0;0;10;;;programmed
151602-0;151602;0;0;10;;;programmed
151603-0;151603;0;0;10;;;programmed
151604-0;151604;0;0;10;;;programmed
151605-0;151605;0;0;10;;;programmed
151606-0;151606;0;0;10;;;programmed
151607-0;151607;0;0;10;;;programmed
...
151622-0;151622;0;0;10;;;programmed
151623-0;151623;0;0;10;;;programmed
151624-0;151624;0;0;10;;;programmed
151625-0;151625;0;0;10;;;programmed
...


filter_file.csv
---
151622-0;151622;0

我想比较这两个文件,并使用第一列值作为比较,创建第三个文件,其中包含与“filter_file.csv”中的行匹配的“main_file.csv”中的行。

如示例所示,我应该得到一个包含一行的“result_file.csv”,不幸的是我得到一个空文件。

专业输出应为:

151622-0;151622;0;0;10;;;programmed

这是我尝试过的通讯:

awk 'BEGIN {FS=OFS=";"} NR==FNR{a[$1]=1; next} a[$1]{print}' filter_file.csv main_file.csv > result_file.csv

如果我理解正确,它应该这样解释:

awk '                           # starting awk program
BEGIN {FS=OFS=";"}              # define column separator as commat for both files (main & filter)
NR==FNR{a[$1]=1; next}          # during read of the first file (filter_file.csv), create an array 'a' with first column value as index
a[$1]{print}                    # during read of the second file (main_file.csv), if first column value exist as an index of the array 'a', print the whole line in the 'result_file.csv'
' 
filter_file.csv main_file.csv   # files to be compared
> result_file.csv               # direct the output to the third file

但恐怕我错过了一些东西:/

编辑:更新以添加一些上下文:

该命令是从 PHP 脚本执行的,如下所示:

$awk_cmd = 'awk \'BEGIN {FS=OFS=";"} NR==FNR {a[$1]=1; next} $1 in a {print}\' ' . $filter_file . ' ' . $ref_file . ' > ' . $match_file;
exec($awk_cmd);

其中 $filter_file、$ref_file 和 $match_file 是文件的完整路径。

EDIT2 :我测试了grep命令并得到以下输出:

0000000   1   5   1   6   2   2   -   0   ;   1   5   1   6   2   2   ;
0000020   0   ;   0   ;   1   0   ;   ;   ;   p   r   o   g   r   a   m
0000040   m   e   d  \r  \n
0000045
Linux 文件 awk

评论

1赞 Dexter0015 8/22/2023
我更新了预期的输出
0赞 markp-fuso 8/22/2023
FWIW:如果不存在,将创建一个新的数组条目(值为 0);虽然这不应该影响功能(在这种情况下),但它将需要更多的内存; 应该提供相同的好处(如果 $1 是数组的索引,则打印),而无需额外的内存使用a[$1]{print}$1 in aa[]
2赞 Ed Morton 8/23/2023
我怀疑您的问题通常是关于如何从 PHP 调用命令,与您的 awk 脚本无关 - 您只是碰巧在调用 awk,但它可能是 sed 或 perl 或其他任何东西。您可能需要转义其他字符或使用其他和/或不同的引号或其他东西。谷歌“如何从PHP调用awk”或类似的东西。
1赞 shellter 8/23/2023
' \r \n' ...这是一个DOS文件,祝你好运!dos2unix filter_file.csv main_file.csv
1赞 Renaud Pacalet 8/23/2023
@shellter我认为这在这里无关紧要。我们不关心DOS或UNIX,只要在这两种格式中最后一个字节都是,我们只对第一个字段感兴趣。不幸的是,由于错误的原因,这个问题被关闭了。\n

答:

2赞 Renaud Pacalet 8/23/2023 #1

您的文件格式显然是 DOS (),但这在这里应该不是问题。下面假设您的 csv 文件是简单的文件(没有多行记录,没有带引号的字段......示例输出以 为前缀。\r\n;-|

使用任何 POSIX :awk

awk -F';' 'NR==FNR {a[$1];next} $1 in a' filter_file.csv main_file.csv
-| 151622-0;151622;0;0;10;;;programmed

-F';'定义为输入字段分隔符。在解析第一个文件时(仅对第一个文件为 true),将第一个字段 () 存储为数组的键并移动到行。在解析第二个文件时,如果第一个字段是数组 () 的键,则打印该行(默认操作)。;NR==FNR$1anexta$1 in a

您还可以使用 和 :sortjoin

join -t';' -o 1.{1..8} <( sort -t';' main_file.csv ) <( sort -t';' filter_file.csv )
-| 151622-0;151622;0;0;10;;;programmed

评论

1赞 Dexter0015 8/23/2023
当有人关闭问题时,我无法添加一个完整的 anwser,但完整的解决方案是首先按照@shelter建议对文件进行编码,然后运行 awk 命令(我修改该命令以按照您的建议设置 seprateor(不使用带双引号的 BEGIN)。谢谢大家!
1赞 Renaud Pacalet 8/23/2023
这里不需要对文件进行编码,只要以 .换句话说,在 DOS 模式下,您的行比在 UNIX 模式下长 1 个字节,但由于您只对第一个字段感兴趣......\r\n\n
0赞 Dexter0015 8/23/2023
我很困惑,我们在测试服务器 (ubuntu) 上部署了代码,但它无法以任何一种方式工作(有或没有编码)
0赞 Renaud Pacalet 8/23/2023
您是否在此测试服务器上单独测试了脚本(没有PHP包装器)?awk
0赞 Dexter0015 8/23/2023
是的,相同的结果:空文件
0赞 Dexter0015 8/28/2023 #2

首先感谢大家的建议!

我自言自语,因为没有一个答案完全解决了这个问题,尽管完整的解决方案是@shelter编码建议和@Renaud Pacalet 重写 awk 命令之间的混合。

我在问题中没有指定的东西(因为当时我没有,尽管它会产生任何影响)是我在 Windows 10 上的本地环境中测试了代码,这是问题的一部分,正如@shelter建议的那样......

因此,在执行 awk 命令之前,我在代码中添加一个步骤来转换要比较的文件:

dos2unix filter_file.csv main_file.csv

我还修改了 Pacalet 建议@Renaud为 awf 指定列分隔符的方式(而不是 ,它给了我以下命令:

awk -F';' 'NR==FNR {a[$1]=1; next} $1 in a {print}' filter_file.csv main_file.csv > result_file.csv

这两个变化结合在一起给了我正确的结果。

在我的 php 脚本中,它给出了如下内容:

$convert = 'dos2unix  ' . $filter_file . ' ' . $ref_file;
exec($convert);

$awk_cmd = 'awk -F\';\' \'NR==FNR {a[$1]=1; next} $1 in a {print}\' ' . $filter_file . ' ' . $ref_file . ' > ' . $match_file;
exec($awk_cmd);

其中使用变量调用文件,因为它包含它的完整路径。

一旦应用了这些 chonges,一切都在我的本地服务器上工作,但是,一个推送到测试服务器 (ubuntu) 我们仍然收到错误。 原来 dos2unix 没有安装在测试服务器上...... Onece 已安装,一切按预期工作。

不过,我不明白为什么我只在过滤器文件只计算一行时才遇到这个问题。当过滤器文件计数几行时,我从未提出过这个问题,奇怪......