比较`n`纯文本文件并打印每个文件的唯一行数

时间:2019-03-29 21:24:04

标签: bash count compare unique

我有@JsonProperty("startDate") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private LocalDate startDate = null; 个纯文本文件,其中有几行文字。
有些文件之间有些行重复。
n中是否有一种方法可以比较文件并打印出与其他文件相比每个文件有多少行?

示例:

bash

我基本上是在寻找一种类似于以下内容的解决方案:
# file1 1 2 3 10 # file2 2 10 50 3 # file3 100 2 1 40 6

2 个答案:

答案 0 :(得分:0)

使用grepsorttruniq n > 1的人:

$ grep ^ file[123] | tr : ' ' | sort -k2 | uniq -f 1 -u
file3 100
file3 40
file2 50
file3 6

另一个使用GNU awk的人:

$ awk '{
    a[$0]++
    f[FILENAME][FNR]=$0
}
END {
    for(i in f)
        for(j in f[i])
            if(a[f[i][j]]==1)
                print i,f[i][j]
}' file[123]
file2 50
file3 100
file3 40
file3 6

答案 1 :(得分:0)

对于任意两个文件,例如file1file2,您可以在file1中输出唯一行(即file1中的行不会出现在{{ 1}}),如下所示:

file2

使用您的> fgrep -vx -f file2 file1 1 file1file2的其他示例:

file3

请注意,在大多数(如果不是全部)系统上,> fgrep -vx -f file3 file1 # Show lines in file1 that do not appear in file3 3 10 > fgrep -vx -f file2 file3 # Show lines in file3 that do not appear in file2 100 1 40 6 实际上只是fgrep的同义词,其中grep -F告诉-F而不是试图比较固定字符串。匹配正则表达式。因此,如果由于某种原因而没有grep,则应该可以使用fgrep而不是grep -Fvx

要比较多个文件,它会比较棘手,但是对于任何给定的文件,您都可以在一个临时文件中保留唯一行的运行列表,然后通过将临时文件与其他文件进行比较来减少它时间:

fgrep -vx

由于只需要计数唯一行数,因此您可以将最后一个命令通过管道传递到# Show all lines in file3 that do not exist in file1 or file2 fgrep -vx -f file1 file3 > file3_unique fgrep -vx -f file2 file3_unique 100 40 6

wc -l

如果使用3个以上的文件执行此操作,则会发现您需要使用额外的临时文件。假设您有一个> fgrep -vx -f file2 file3_unique | wc -l 3

file4

这意味着您将需要第三条> cat file4 1 3 40 6 命令来完成唯一行列表。如果只是这样做,就会遇到问题:

fgrep

换句话说,您无法将结果通过管道传输回# Show all lines in file3 that do not exist in file1, file2, or file4 > fgrep -vx -f file1 file3 > file3_unique > fgrep -vx -f file2 file3_unique > file3_unique grep: input file 'file3_unique' is also the output 版本的同一文件。因此,您需要每次输出到一个单独的临时文件,然后再对其进行重命名:

grep

请注意,我在最后一行省略了# Show all lines in file3 that do not exist in file1, file2, or file4 > fgrep -vx -f file1 file3 > temp > mv temp file3_unique > fgrep -vx -f file2 file3_unique > temp > mv temp file3_unique > fgrep -vx -f file4 file3_unique 100 ,只是为了表明它可以按预期工作。

当然,如果您的文件数是任意的,则需要循环进行比较:

| wc -l

这将产生输出:

files=( file* )
for ((i=0; i<${#files[@]}; ++i)); do
  cp -f "${files[i]}" unique
  for ((j=0; j<${#files[@]}; ++j)); do
     if (( j != i )); then
       fgrep -vx -f "${files[j]}" unique > temp
       mv temp unique
     fi
  done
  echo "${files[i]}:$(wc -l <unique)"
  rm unique
done

如果file1:0 file2:1 file3:1 file4:0 temp是现有文件或目录,则可能要考虑使用unique。例如:

mktemp

这样,实际文件将类似于unique=$(mktemp) temp=$(mktemp) fgrep -vx file2 file3 > "$temp" mv "$temp" "$unique" 等,并且您不会在运行此代码的目录中意外覆盖名为/tmp/tmp.rFItj3sHVQtemp的文件。

更新:只是为了踢球,我决定缩小一点。一方面,我不太喜欢嵌套循环或临时文件。这是一个兼而有之的版本。此改进基于以下观察:通过依次与uniquefile1file2比较来降低file3的含义与进行单个比较是相同的file4file1 + file2 + file3的串联之间。然后,诀窍只是弄清楚如何串联每个其他文件而不会循环。但事实证明,实际上,您可以使用数组拼接在bash中相当轻松地完成此操作。例如:

file4

将此与先前的解决方案结合起来,我们可以用单行替换内部循环和临时文件:

files=( file1 file2 file3 file4 )

# Concatenate all files *except* ${files[2]}, i.e., file3
> cat "${files[@]:0:2}" "${files[@]:3}"
1
2
3
10
2
10
50
3
1
3
40
6