【问题标题】:awk :: how to find matching words in two filesawk :: 如何在两个文件中查找匹配的单词
【发布时间】:2021-02-04 20:06:27
【问题描述】:

StackOverflow 上的一些好心人帮助我使用 awk 找到两个文件中的共同行:

awk 'NR==FNR{a[tolower($0)]; next} tolower($0) in a' 1.txt 2.txt

但是如何在两个单词对齐的文件中找到常用单词呢?

例如,假设我有 1.txt 和这些词:

apple
orange
butter
flower

然后2.txt 用这些话:

dog cat Butter tower

如何返回butterButter

我只想找常用词。

【问题讨论】:

    标签: awk


    【解决方案1】:

    给定:

    $ cat file1
    apple 
    orange 
    butter
    flower
    
    $ cat file2
    dog cat Butter tower
    

    我会这样写:

    awk 'FNR==NR{for(i=1;i<=NF;i++) words[tolower($i)]; next}
         {for (i=1;i<=NF;i++) if (tolower($i) in words) print $i}
    ' file1 file2 
    

    请注意,在FNR==NR 的情况下,有一个逐字段循环处理每行可能包含多个单词的文件。如果您知道情况并非如此,您可以简化为:

    awk 'FNR==NR{words[tolower($1)]; next}
         {for (i=1;i<=NF;i++) if (tolower($i) in words) print $i}
    ' file1 file2 
    

    如果这在 Windows 上不起作用,则可能是 \r\n 行结尾的问题。如果awk 使用RS=[\n] 值,则\r 将留在行尾的所有单词上; butter\rbutter 不匹配。

    试试:

    awk -v RS='[ \r\n\t]' 'FNR==NR{words[tolower($0)]; next}
                           tolower($0) in words' file1 file2 
    

    在链接中对您的 WSL cmets 的评论:

    您在 DOS 上处理 Unix 文件的解决方法有很多。

    以这种方式创建带有 DOS 行结尾的 file1

    $ printf 'apple\r\norange\r\nbutter\r\nflower\r\n' >file1 
    

    现在您可以测试/查看文件以cat -v 结尾的那些行:

    $ cat -v file1
    apple^M
    orange^M
    butter^M
    flower^M
    

    您还可以删除带有sedperlawk 等的行结尾。这是从文件中删除\rawk

    $ cat -v <(awk 1 RS='\r\n' ORS='\n' file1)
    apple
    orange
    butter
    flower
    

    sedperl

    $ cat -v <(sed 's/\r$//' file1)
    #same
    

    $ cat -v <(perl -0777 -lpe 's/\r\n/\n/g' file1)
    

    等等。然后在 awk-on-windows 中使用相同的构造:

    awk 'your_awk_program' <(awk 1 RS='\r\n' ORS='\n' file1) <(awk 1 RS='\r\n' ORS='\n' file2)
    

    缺点:虽然每个输入都被视为不同的逻辑文件,所以 FNR==NR awk 测试仍然有效,但 awk 特殊变量 FILENAME 在此过程中丢失。如果您想保持FILENAME 与实际文件相关联,您需要在馈送到 awk 之前对文件进行预处理,或者在 awk 脚本中处理 \r

    【讨论】:

    • 谢谢,我很确定在 StackOverflow 上有 77.8k 分的人知道他的东西。不幸的是,你的代码什么也没给我:snipboard.io/6YUOg1.jpg。我在 Windows10 上使用 WSL2,如果你告诉我这对你有用,这看起来像是 WSL2 上的错误?你怎么看
    • 您是否在 Linux(不是 WSL)上针对相同的 2 个文件进行了测试?
    • 它绝对适用于 Mac OS。您可能的问题是 Windows '\r\n' 行尾,并且文件在您的 awk 上没有被正确解释。
    • 现已测试,您的最后一个代码正在运行!这绝对是 Windows 上 WSL 中 '\r\n' 行尾的问题
    【解决方案2】:

    这个grep 应该可以完成这项工作:

    grep -oiwFf 1.txt 2.txt
    
    Butter
    

    否则这个简单的gnu awk 也可以工作:

    awk -v RS='[[:space:]]+' 'NR==FNR {w[tolower($1)]; next} tolower($1) in w' 1.txt 2.txt
    
    Butter
    

    【讨论】:

    • 我能听到你@anubhava 的声音,感谢你的帮助,看起来这两个命令都不起作用:snipboard.io/XA6PHO.jpg 如果你在其他两个回复中看到 cmets,你会看到我们有显然,WSL 处理\r\n 行尾的方式很奇怪。我可能会将此标记给 Microsoft,因为它本身就是很多 shell 命令,如 grepawk 将无法使用。让我知道你的想法
    • 这很好@FrancescoMantovani
    • 顺便说一句,我建议的 awk 命令适用于以 \r\n 结尾的文件,因为 [[:space:]] 也匹配 \r
    【解决方案3】:

    您需要遍历每行(2.txt)的每个字段并检查:

    awk 'NR==FNR{a[tolower($0)];next}{for(i=1;i<=NF;i++){if(tolower($i) in a){print $i}}}' \
        1.txt 2.txt
    

    在 awk 中执行此操作的另一种方法是在处理第二个文件时向输入记录分隔符添加空格:

    awk 'NR==FNR{a[tolower($0)];next} tolower($0) in a' 1.txt  RS="[\n ]" 2.txt
    

    【讨论】:

    • 对你来说也是如此,感谢你的帮助和 131k 在 StackOveflow 上自己的谈话,我想我在 Windows10 上的 WSL2 上遇到了awk 的错误:snipboard.io/MiHhyW.jpg
    • 您能否确认您的命令在 Linux(而非 WSL)上对相同的 2 个文件有效?
    • 使用dos2unix 1.txt 2.txt 确保您的数据文件是干净的。祝你好运。
    • 嗨,不错的提示,我现在使用 dos2unix 进行了测试,但问题不存在:snipboard.io/MODBic.jpg
    • 嘿@FrancescoMantovani!很抱歉没有早点回答,我会看看的
    猜你喜欢
    • 2022-01-14
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 2020-05-09
    • 1970-01-01
    • 2013-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多