grep 文件中的 20k 个单词

grep 文件中的 20k 个单词

我的文件夹中有 5000 个文件。这些文件被命名为 XX0000001 到 XX0005000

我正在尝试从每个文件中获取单词,然后 grep 它们以及另一个文件中的下一行(target.txt)

我的一些 XX* 文件中有大约 30,000 个单词

有什么办法可以做到这一点吗?

我努力了 :

start_number=0000001
end_number=0005000
words_file=target.txt
output_folder="output_results"
mkdir -p "$output_folder"
for ((i=start_number; i <=end_number; i++)); do
   filename="XX$(printf "%07d" "$i")"
   output_file="$output_folder/output_${filename}.txt"
   while read -r word; do
      awk -v word="$word" '{for (i=1; i<=NF; i++) if($1 ~ word) {print; next}}' "$filename" >> "$output_file"
   done < "$words_file"
done

有没有更快的方法来做到这一点?我的目标文件有数百万行需要搜索;精确目标文件大小为 20 GB,106441678 行

例如:XX0000001 文件如下所示:

Big1 Big5 Big7 Big10 Big11

(还有很多很多的字;一些 XX 文件甚至可能有多达 30k 字)

Target.txt 文件如下所示:

#Big1

This_is_a_file_containing_xxxxx

#Big2

This_is_a_file_containing_xxxxx

#Big3

This_is_a_file_containing_xxxxx

#Big4

This_is_a_file_containing_xxxxx

#Big5

This_is_a_file_containing_xxxxx

#Big6

This_is_a_file_containing_xxxxx

#Big7

This_is_a_file_containing_xxxxx

#Big8

This_is_a_file_containing_xxxxx

#Big9

This_is_a_file_containing_xxxxx

#Big10

This_is_a_file_containing_xxxxx

#Big11

This_is_a_file_containing_xxxxx

#Big12

This_is_a_file_containing_xxxxx

答案1

我的文件夹中有 5000 个文件。这些文件被命名为 XX0000001 到 XX0005000

要循环一组文件,请使用 shell glob,例如for f in XX0*此处。如果您确实需要循环一组数字,则需要注意前导零,就像在 Bash 中一样,它们将数字标记为八进制。例如尝试你的循环,但只是打印数字并查看最后一个:

start_number=0000001
end_number=0005000
for ((i=start_number; i <=end_number; i++)); do
   echo $i
done |tail -1

我们看到输出是2560,不是5000。不过,zsh 中的情况并非如此,并且您没有提及您正在运行哪个 shell,但是这个问题可能值得指出。


这里,

   while read -r word; do
      awk -v word="$word" '{for (i=1; i<=NF; i++) if($1 ~ word) {print; next}}' "$filename" >> "$output_file"
   done < "$words_file"

我不太确定这是做什么的,但我只是注意到,在 AWK 脚本中,您循环遍历输入行的所有字段,但随后您只引用$1循环内的字段 1 ( )。


现在,我假设您的文件如下所示:

% cat XX0000001
Big1 Big7
% cat XX0000002
Big5 Big10
% cat target.txt
#Big1
This_is_a_file_containing_xxxxx
#Big2
This_is_a_file_containing_xxxxx
[...]

即文件中的一行中有多个不同的模式(例如Big1和) (而不是例如每行一个)。另外,我想您想找到与任何模式匹配的行,然后将它们与下一行一起打印。Big7XX0*target.txt

现在,标准 grep 可以在匹配后打印“多一行”,并且可以同时查找多个模式。该-f选项采用文件的名称,该文件的行形成模式,因此您必须预处理文件XX0*以使每个模式显示为单行。您可以通过将所有空格更改为换行符来做到这一点tr。最简单的方法是使用进程替换将trto的输出grep作为文件使用,但您也可以使用临时文件(或者甚至可以将输出通过管道传输到trto grep -f -

例如:

% grep -A1 -f <(tr ' ' '\n' < XX0000001 ) target.txt
#Big1
This_is_a_file_containing_xxxxx
--
#Big7
This_is_a_file_containing_xxxxx
--
#Big10
This_is_a_file_containing_xxxxx

当然,该模式Big1也出现在该行上#Big10,因此它是匹配的。 (但是您可以尝试-w使用 grep 选项来请求全字匹配。)如果您想删除--分隔符,您可以通过管道传输结果grep -ve --

其效率如何可能取决于您的 grep 实现,但作为一个为此目的而设计的工具,它可能比在 shell 脚本中执行相同的操作有更好的机会进行优化。 Shell 脚本速度很慢。如果所有模式均为 格式Big*,明智的做法是仅查找公共部分一次。也许可以将模式列表更改为单个模式,Big(1|5|7|10)希望它能更好地为正则表达式引擎工作。

答案2

您正在一个非常大的文件中搜索大量搜索词,您不太可能在 shell/标准工具中找到“快速”解决方案。也就是说,我认为你的方法效率特别低。

也许是这样的(未经测试):

words_file=target.txt
output_folder="output_results"
mkdir -p "$output_folder"
for filename in XX* ; do 
  output_file="$output_folder/output_${filename}.txt"
  grep -f "$filename" -Fx -A1 "$words_file" > "$output_file"
done

但坦率地说,我也不认为这会很快。

这可能是多线程的,这可能会提高吞吐量。

这是一个多线程替代方案。您需要将该threads值调整为适合您的操作环境的值。

threads=4
words_file=target.txt
output_folder="output_results"
mkdir -p "$output_folder"

find . -maxdepth 1 -type f -name 'XX*' -print0 | \
  xargs -I% -0 -P$threads bash -c '
    file="%"
    output_file="$output_folder/output_${file#./}.txt"
    grep -f "%" -Fx -A1 "$words_file" > "$output_file"
  '

从中获得的任何好处都将取决于硬件因素,例如可用内存、CPU 核心数量、存储速度以及服务器上的其他活动。

答案3

您的问题不清楚,并且您没有提供任何预期的输出,这无助于澄清您的需求,并且无法测试潜在的解决方案以查看它是否有效,但这可能是您想要的,使用任何 awk :

awk '
    FILENAME != ARGV[ARGC-1] {
        for ( i=1; i<=NF; i++ ) {
            words[$i]
        }
        next
    }
    f {
        print
    }
    {
        f = 0
        for ( word in words ) {
            if ( $0 ~ word ) {
                print
                f = 1
                next
            }
        }
    }
' some_folder/XX* target.txt

如果您的XX*文件过多,ARG_MAX则将其更改为:

printf '%s\n' some_folder/XX* |
awk '
    FILENAME == "-" {
        ARGV[ARGC++] = $0
        next
    }
    FILENAME != ARGV[ARGC-1] {
        for ( i=1; i<=NF; i++ ) {
            words[$i]
        }
        next
    }
    f {
        print
    }
    {
        f = 0
        for ( word in words ) {
            if ( $0 ~ word ) {
                print
                f = 1
                next
            }
        }
    }
' - target.txt

相关内容