Bash根据正则表达式匹配组合两个文本文件

时间:2017-02-02 23:31:08

标签: bash awk

Haven没有看到类似的解决方案......

我有两个文件,每个文件都包含一个文件名列表。文件内容有重叠,但文件A包含文件B中不存在的文件名。另外,文件A和B中的文件扩展名不同。即:

A                     B
------------          --------------
file-1-2.txt          file-1-2.png
file-2-3.txt          file-3-4.png
file-3-4.txt
...

如何将逗号分隔的两个文件合并为一个忽略不匹配的行?

那是:

C
------------
file-1-2.txt,file-1-2.png
file-3-4.txt,file-3-4.png

我认为类似于以下内容的awk的一些用法将起作用:

awk 'FNR==NR{NOT SURE} {print $1,$2}' fileA fileB

提前致谢!

4 个答案:

答案 0 :(得分:2)

这个纯粹的bash解决方案应该能够处理并处理任一文件中的点,反斜线,破折号和其他特殊字符。

mapfile -t arr_a < A
mapfile -t arr_b < B

for a in "${arr_a[@]}"; do for b in "${arr_b[@]}"; do
    [[ ${a%.*} == "${b%.*}" ]] && printf '%s,%s\n' "$a" "$b" && break
done; done

首先,我们使用mapfile将文件内容读入数组,每个项目一行。 1 然后,对于A中的每一行,我们将与B中的每一行进行比较。

为了仅比较扩展之前的部分,我们使用shell参数扩展${var%pattern},它从结束时删除了glob .* 2 的最短匹配。文件名。

1 -t选项从数组项中删除尾部换行符。

2 这里的.是文字的,删除了一个句号和之后的所有内容。

答案 1 :(得分:1)

你可以这样做:

$ awk 'function base(fn) {sub("[.][^.]*$", "", fn); return fn} 
       NR==FNR { fn[$1]; next} 
       {for (e in fn){ if (base(e)==base($1)){ printf "%s,%s\n", e, $1 }}} ' f1 f2
file-1-2.txt,file-1-2.png
file-3-4.txt,file-3-4.png

由于awk关联数组是无序的,因此打印输出的顺序由第二个文件的顺序决定 - 而不是第一个。

说明:

  1. function base(fn) {sub("[.][^.]*$", "", fn); return fn}是一个从文件名中删除扩展名的函数(假设扩展名是找到的最后一个.右侧的非.个字符。返回整个名称如果没有找到.。)
  2. NR==FNR { fn[$1]; next}将每一行(在本例中为每个文件名)读入一个关联数组。 NR==FNRawk成语,仅对第一个文件为真,next表示在第一个文件名文件上执行此部分。由于前导和尾随空格被剥离,因此使用$1。由于Unix文件名可以具有前导或尾随空格,因此这是您需要解决的罕见歧义。如果您不想剥离线条,则可以使用$0代替。
  3. {for (e in fn){ if (base(e)==base($1)){ printf "%s,%s\n", e, $1 }}}现在用于除第一个文件以外的任何行(其中NR==FNR为真,因为next跳过此部分)循环保存的文件名。如果基本名称相同则打印。

答案 2 :(得分:0)

unix join命令应该做你想要的。设置字段分隔符-t'。'成为一个点并通过两个文件中的第一列连接。您可能需要提前对文件进行排序。可以使用正确的语法在与连接相同的命令行上完成排序。 &lt;(sort -k 2 file1.txt)&lt;(sort file2.txt)

答案 3 :(得分:0)

这是一种相当强大的力量:

file1="file1.txt"
file2="file2.txt"
out_file="out.txt"
touch $out_file
while read line ; do  # read the first file line by line
  file1_name="$(echo "$line" | cut -d'.' -f1)"    # get the filename without extension
  file2_name="$(grep "$file1_name\." $file2)"
  if [ -n "$file2_name" ]; then   #did we find a match
    echo "$line,$file2_name" >> $out_file
  else
    echo "Did not find a match to ${line} in $file2"
  fi
done < $file1 

我们遍历file1并在文件2中查找匹配项。如果找到,我们输出到输出文件。

其他改进:使用正则表达式更好的grep:

file2_name="$(grep -e "$file1_name\.[^.]*$" $file2)"

这会查找以$file1_name开头的一行,一个点.,然后不再有点,直到结尾为扩展名。