5

我正在尝试使用 shell 脚本(以及“一个衬里”)来查找大约 50 个文件之间的任何公共行。 编辑:注意我正在寻找出现在所有文件中的一行(行)

到目前为止,我已经尝试过 grep grep -v -x -f file1.sp *,它只匹配所有其他文件中的文件内容。

我也尝试过grep -v -x -f file1.sp file2.sp | grep -v -x -f - file3.sp | grep -v -x -f - file4.sp | grep -v -x -f - file5.sp等...但我相信使用要作为 STD 搜索的文件的搜索不是要匹配的模式。

有谁知道如何使用 grep 或其他工具来做到这一点?

我不介意它是否需要一段时间才能运行,我必须在大约 500 个文件中添加几行代码,并希望在每个文件中找到一个共同的行以便插入“之后”(它们最初是只是来自一个文件的 c&p,所以希望有一些共同点!)

谢谢你的时间,

4

4 回答 4

3

当我第一次读到这篇文章时,我以为您正在尝试找到“任何共同点”。我认为这是“查找重复行”的意思。如果是这种情况,以下内容就足够了:

sort *.sp | uniq -d

重新阅读您的问题后,您似乎实际上是在尝试查找“出现在所有文件中”的行。如果是这种情况,您将需要知道目录中的文件数:

find . -type f -name "*.sp" | wc -l

如果这返回数字 50,那么您可以awk像这样使用:

WHINY_USERS=1 awk '{ array[$0]++ } END { for (i in array) if (array[i] == 50) print i }' *.sp

您可以合并此过程并编写这样的单行代码:

WHINY_USERS=1 awk -v find=$(find . -type f -name "*.sp" | wc -l) '{ array[$0]++ } END { for (i in array) if (array[i] == find) print i }' *.sp
于 2012-09-03T23:06:29.730 回答
2

旧的 bash 答案 (O(n); 打开2 * n文件)

从@mjgpy3 答案,您只需要创建一个 for 循环并使用comm,如下所示:

#!/bin/bash

tmp1="/tmp/tmp1$RANDOM"
tmp2="/tmp/tmp2$RANDOM"

cp "$1" "$tmp1"
shift
for file in "$@"
do
    comm -1 -2 "$tmp1" "$file" > "$tmp2"
    mv "$tmp2" "$tmp1"
done
cat "$tmp1"
rm "$tmp1"

保存在 a 中comm.sh,使其可执行,然后调用

./comm.sh *.sp 

假设你所有的文件名都以.sp.

更新的答案,python,每个文件只打开一次

查看其他答案,我想给出一个在不使用任何临时文件的情况下打开每个文件一次,并支持重复行的答案。此外,让我们并行处理文件。

给你(在python3中):

#!/bin/env python
import argparse
import sys
import multiprocessing
import os

EOLS = {'native': os.linesep.encode('ascii'), 'unix': b'\n', 'windows': b'\r\n'}

def extract_set(filename):
    with open(filename, 'rb') as f:
        return set(line.rstrip(b'\r\n') for line in f)

def find_common_lines(filenames):
    pool = multiprocessing.Pool()
    line_sets = pool.map(extract_set, filenames)
    return set.intersection(*line_sets)

if __name__ == '__main__':
    # usage info and argument parsing
    parser = argparse.ArgumentParser()
    parser.add_argument("in_files", nargs='+', 
            help="find common lines in these files")
    parser.add_argument('--out', type=argparse.FileType('wb'),
            help="the output file (default stdout)")
    parser.add_argument('--eol-style', choices=EOLS.keys(), default='native',
            help="(default: native)")
    args = parser.parse_args()

    # actual stuff
    common_lines = find_common_lines(args.in_files)

    # write results to output
    to_print = EOLS[args.eol_style].join(common_lines)
    if args.out is None:
        # find out stdout's encoding, utf-8 if absent
        encoding = sys.stdout.encoding or 'utf-8'
        sys.stdout.write(to_print.decode(encoding))
    else:
        args.out.write(to_print)

将其保存到find_common_lines.py, 并调用

python ./find_common_lines.py *.sp

--help该选项的更多使用信息。

于 2012-09-04T00:07:09.773 回答
2

结合这两个答案(ans1ans2)我认为您无需对文件进行排序即可获得所需的结果:

#!/bin/bash
ans="matching_lines"

for file1 in *
do 
    for file2 in *
        do 
            if  [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then
                echo "Comparing: $file1 $file2 ..." >> $ans
                perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
            fi
         done 
done

只需保存它,赋予它执行权限 ( chmod +x compareFiles.sh) 并运行它。它将获取当前工作目录中存在的所有文件,并将进行全部比较,并将结果留在“matching_lines”文件中。

需要改进的地方:

  • 跳过目录
  • 避免将所有文件比较两次(file1 vs file2 和 file2 vs file1)。
  • 也许在匹配字符串旁边添加行号

希望这可以帮助。

最好的,

艾伦·卡尔波夫斯基

于 2013-11-08T14:28:04.423 回答
1

看到这个答案。我最初虽然diff听起来像你所要求的,但这个答案似乎更合适。

于 2012-09-03T11:39:53.590 回答