比较两个文件并根据匹配条件生成另一个文件

比较两个文件并根据匹配条件生成另一个文件

我有两个文件,a.txt 和 b.txt,其中 a.txt 包含以“zn”开头的行,例如 zn12c5b 或 zn4i8l,而 b.txt 包含以模式“/number”结尾的行,例如 t17v11/ 112 或 12c5b/450。

我的目标是在 Final.txt 中写入 a.txt 中的字符串(但没有“zn”),这些字符串与 b.txt 中的字符串(没有尾随的“/number”模式)不匹配。

例如:

a.txt:

zn12c5b
zn4i8l

b.txt:

t17v11/112
12c5b/450
4i8ls/681

我应该在 Final.txt 中获得以下输出:

4i8l

注意:文件 a.txt 中的 4i8l(不带“zn”前缀)不等于文件中的 4i8ls(不带“/681”后缀)。我使用的是Ubuntu系统。

答案1

$ awk -F'/' 'NR==FNR{b[$1]; next} {sub(/^zn/,"")} !($1 in b)' b.txt a.txt
4i8l

答案2

以下 bash 脚本应该可以完成这项工作(从 bash 4 或更高版本开始):

#!/bin/bash
readarray -t a_arr < a.txt
readarray -t b_arr < b.txt

for a_el in "${a_arr[@]}"
do 
    # remove the first two characters 'zn'
    substr_a=${a_el:2}
    isin=0
    for b_el in "${b_arr[@]}"
    do  
        # extract matches from b.txt file
        substr_b=$(echo $b_el | sed -n "s#^\(.*\)\/[[:digit:]]\+#\1#p")
        if [ "$substr_a" == "$substr_b" ];then isin=1; break;fi
    done
    if [ $isin -eq 0 ];then echo $substr_a ;fi
done > final.txt

如果您使用低于 4 的 bash 版本,则应将这些readarray行替换为以下行:

a_arr=($(<a.txt))
b_arr=($(<b.txt))

答案3

像这样丑陋的网民

sdiff -s <(sed 's/^zn//' a.txt|sort) <(awk -F/ '{ print $1 }' b.txt| sort) | awk -F'[<|>]' '{ print $1 }'
#1         #2                          #3                                    #4 

会做这项工作。

解释:

#1sdiff逐行比较 2 个文本文件,-s 选项抑制公共行

#2sed从 a.txt 文件每行的开头去除 zn 前缀

#3被告知仅输出b.txt 文件中每一行的awk左侧部分/

< |#4 另一个 awk 被告知仅输出字符串或字符左侧的部分>。这些字符是sdiff输出中的分隔符。

<(somecommand) 成语又名流程替代被视为具有由某些命令输出生成的内容的文件名

| sort显然,对输出进行排序以提供sdiff排序的文件

答案4

如果你有GNU awk(它实现了BEGINFILE)那么你可以......

awk 'BEGINFILE{FS=(NR!=0)?"^..":"/"}
     NR==FNR{test[$1]=1; next}
     !($2 in test){print $2}' b.txt a.txt

418l

如果您想写回而a.txt不是重定向到新文件,> final.txt那么您可以添加

| sponge a.txt

演练

awk 'BEGINFILE{FS=(NR!=0)?"^..":"/"}

当第一个文件打开时BEGINFILE执行,NR==0所以FS=/

     NR==FNR{test[$1]=1; next}

迭代NR==FNR加载$1到测试数组中的第一个文件

当第二个文件打开时BEGINFILE再次执行,但现在NR!=0FS=^..输入行的前两个字符之后的所有内容删除到$2

     !($2 in test){print $2}' b.txt a.txt

迭代第二个文件,print $2如果它不在测试数组中!($2 in test)

相关内容