如何根据行的第一个单词替换同一文件中的多个模式?
How to replace multiple patterns in the same file, based on the line's first word?
我在一个文件中有一个短语列表(“phrases
”),每个短语都在自己的行上。
我还有另一个文件,其中包含一个单词列表,每个单词占一行 ("words
")。
我希望在“phrases
”中以“words
”中列出的单词开头的每个短语的末尾附加一个星号。
例如:
文件“phrases
”:
gone are the days
hello kitty
five and a half
these apples are green
文件“words
”:
five
gone
操作后“phrases
”的预期结果:
gone are the days *
hello kitty
five and a half *
these apples are green
到目前为止我所做的是:
parallel -j0 -a words -q perl -i -ne 'print " *" if /^({}\s.*)$/' phrases
但这会截断文件,有时(并非总是)会出现此错误:
Can't remove phrases: No such file or directory, skipping
file.
因为编辑将同时进行,我的目的是让它只搜索和替换以单词开头的那些行,同时保持其他行不变。否则 parallel
并发执行会互相覆盖。
我也对其他并发方法持开放态度。
为什么这个问题不是并行进程调度?
想象一下价值的内部依赖链,需要在流程输出端以给定的、严格的 [SEQ]
控制方式输出。
事实 1 )
虽然使用 [=31= 很容易分拆多个流程] 语法使它在 shell 级别强制执行,这根本不意味着这种共存进程的每一种情况都允许平滑和智能的“just”-by -巧合-[CONCURRENT]
甚至是真正的-[PARALLEL]
免费流程安排。
事实号 2 )
file:phrases
必须在纯 [SERIAL]
方式,因为自然顺序 ( SEQ
) 很重要并且必须保留,即使对于基于文件的结果输出也是如此.
事实 3)
每个基于文件的设计都是纯 [SERIAL]
过程,两者都不是 "just"-[CONCURRENT]
, nor true-[PARALLEL]
,直到有人发明了一种方法,如何使硬盘读取设备的磁头在同一时刻出现在多个位置(这甚至远远超出了亚原子尺度上的量子纠缠和叠加技巧和魔法)。
事实 4 )
当然,可以想象某种 space 用于并发处理,一旦 [SEQ]
- 来自 file:phrases
的读取输入是已知的,如果将处理多个([SEQ]
-operated)查找,则可能会出现一些加速 - 但是,同样,基于条件,那里都是资源(为了同时进行多个查找,如果不是所有并发进程都无缝执行,则不会对进程流产生任何不利影响)并且所有这些都必须“预缓存”整个“已知” -be"-static-content of file:words
(否则无济于事),从而变得有点能够逃脱下一个([SEQ]
-again ) pure-[SERIAL]
fileIO
-[SEQ]
-ordered和concurrent-capacity restricted的再处理第一个词匹配查找,现在某种形式的 gnu-parallel 语法强制要求发生在多个 words
- 爬行过程中。
一个人可以很容易地支付比以往任何时候都多的钱:
不正确甚至幼稚的进程调度可能并且确实会引入附加成本,这在纯 [SERIAL]
代码执行中是从未见过的。即使是最轻量级的并发框架附加成本(并且这些成本确实与 N 成比例,如果许多并发代码执行似乎变得势在必行,字面上不惜一切代价)
因此,始终检查问题依赖链中的所有 [SEQ]
依赖关系。
因此,在梦想性能之前,请务必检查所有 [PAR]
-附加开销。
perl -i -pe'
BEGIN {
my $words_qfn = shift(@ARGV);
open(my $words_fh, "<", $words_qfn) or die $!;
chomp( my @words = <$words_fh> );
my $alt = join "|", map quotemeta, @words;
$re = qr/^(?:$alt)\b.*\K/;
}
s/$re/ */;
' words phrases
这不太适合并行处理,因为到目前为止,您可以执行的最昂贵的操作(通常)是从磁盘读取。 CPU 快得多。
您的问题并不 CPU 密集,因此您不会从并行 运行 中获得太多优势。更糟糕的是 - 正如您所发现的那样 - 您会引发可能导致文件破坏的竞争条件。
实际上,磁盘 IO 是以块的形式完成的 - 多个 K - 来自磁盘,将其提取到缓存中,然后以您可以假装 read
工作的方式提供给 OS逐字节。
如果您按顺序读取文件,预测性提取可以让 OS 更加高效,并且尽可能快地将整个文件拉入缓存,从而大大加快处理速度。
尝试并行化和交织此过程充其量是没有效果的,而且会使事情变得更糟。
所以考虑到这一点,你最好不要尝试并行,而是:
#!/usr/bin/env perl
use strict;
use warnings;
open ( my $words_fh, '<', 'words' ) or die $!;
my $words = join '|', map { s/\n//r } <$words_fh>;
$words = qr/\b(?:$words)\b/;
close ( $words_fh );
print "Using match regex of: ", $words, "\n";
open ( my $phrases_fh, '<', 'phrases' ) or die $!;
while ( <$phrases_fh> ) {
if (m/$words/) {
s/$/ */;
}
print;
}
将输出重定向到所需位置。
最昂贵的部分是读取文件 - 它只执行一次。为每个搜索词的同一行重复调用正则表达式引擎也很昂贵,因为您将执行 N * M 次,其中 N 是单词数,M 是行数。
因此,我们编译一个正则表达式,并使用零宽度 \b
字边界标记匹配它(因此它不会匹配子字符串)。
注意 - 我们不引用 words
的内容 - 这可能是错误或功能,因为这意味着您可以将正则表达式添加到组合中。 (当我们编译正则表达式时,这可能会中断)。
如果你想确保它是'literal',那么:
my $words = join '"', map { quotemeta } map { s/\n//r } <$words_fh>;
我在一个文件中有一个短语列表(“phrases
”),每个短语都在自己的行上。
我还有另一个文件,其中包含一个单词列表,每个单词占一行 ("words
")。
我希望在“phrases
”中以“words
”中列出的单词开头的每个短语的末尾附加一个星号。
例如:
文件“phrases
”:
gone are the days
hello kitty
five and a half
these apples are green
文件“words
”:
five
gone
操作后“phrases
”的预期结果:
gone are the days *
hello kitty
five and a half *
these apples are green
到目前为止我所做的是:
parallel -j0 -a words -q perl -i -ne 'print " *" if /^({}\s.*)$/' phrases
但这会截断文件,有时(并非总是)会出现此错误:
Can't remove phrases: No such file or directory, skipping file.
因为编辑将同时进行,我的目的是让它只搜索和替换以单词开头的那些行,同时保持其他行不变。否则 parallel
并发执行会互相覆盖。
我也对其他并发方法持开放态度。
为什么这个问题不是并行进程调度?
想象一下价值的内部依赖链,需要在流程输出端以给定的、严格的 [SEQ]
控制方式输出。
事实 1 )
虽然使用 [=31= 很容易分拆多个流程] 语法使它在 shell 级别强制执行,这根本不意味着这种共存进程的每一种情况都允许平滑和智能的“just”-by -巧合-[CONCURRENT]
甚至是真正的-[PARALLEL]
免费流程安排。
事实号 2 )
file:phrases
必须在纯 [SERIAL]
方式,因为自然顺序 ( SEQ
) 很重要并且必须保留,即使对于基于文件的结果输出也是如此.
事实 3)
每个基于文件的设计都是纯 [SERIAL]
过程,两者都不是 "just"-[CONCURRENT]
, nor true-[PARALLEL]
,直到有人发明了一种方法,如何使硬盘读取设备的磁头在同一时刻出现在多个位置(这甚至远远超出了亚原子尺度上的量子纠缠和叠加技巧和魔法)。
事实 4 )
当然,可以想象某种 space 用于并发处理,一旦 [SEQ]
- 来自 file:phrases
的读取输入是已知的,如果将处理多个([SEQ]
-operated)查找,则可能会出现一些加速 - 但是,同样,基于条件,那里都是资源(为了同时进行多个查找,如果不是所有并发进程都无缝执行,则不会对进程流产生任何不利影响)并且所有这些都必须“预缓存”整个“已知” -be"-static-content of file:words
(否则无济于事),从而变得有点能够逃脱下一个([SEQ]
-again ) pure-[SERIAL]
fileIO
-[SEQ]
-ordered和concurrent-capacity restricted的再处理第一个词匹配查找,现在某种形式的 gnu-parallel 语法强制要求发生在多个 words
- 爬行过程中。
一个人可以很容易地支付比以往任何时候都多的钱:
不正确甚至幼稚的进程调度可能并且确实会引入附加成本,这在纯 [SERIAL]
代码执行中是从未见过的。即使是最轻量级的并发框架附加成本(并且这些成本确实与 N 成比例,如果许多并发代码执行似乎变得势在必行,字面上不惜一切代价)
因此,始终检查问题依赖链中的所有 [SEQ]
依赖关系。
因此,在梦想性能之前,请务必检查所有 [PAR]
-附加开销。
perl -i -pe'
BEGIN {
my $words_qfn = shift(@ARGV);
open(my $words_fh, "<", $words_qfn) or die $!;
chomp( my @words = <$words_fh> );
my $alt = join "|", map quotemeta, @words;
$re = qr/^(?:$alt)\b.*\K/;
}
s/$re/ */;
' words phrases
这不太适合并行处理,因为到目前为止,您可以执行的最昂贵的操作(通常)是从磁盘读取。 CPU 快得多。
您的问题并不 CPU 密集,因此您不会从并行 运行 中获得太多优势。更糟糕的是 - 正如您所发现的那样 - 您会引发可能导致文件破坏的竞争条件。
实际上,磁盘 IO 是以块的形式完成的 - 多个 K - 来自磁盘,将其提取到缓存中,然后以您可以假装 read
工作的方式提供给 OS逐字节。
如果您按顺序读取文件,预测性提取可以让 OS 更加高效,并且尽可能快地将整个文件拉入缓存,从而大大加快处理速度。
尝试并行化和交织此过程充其量是没有效果的,而且会使事情变得更糟。
所以考虑到这一点,你最好不要尝试并行,而是:
#!/usr/bin/env perl
use strict;
use warnings;
open ( my $words_fh, '<', 'words' ) or die $!;
my $words = join '|', map { s/\n//r } <$words_fh>;
$words = qr/\b(?:$words)\b/;
close ( $words_fh );
print "Using match regex of: ", $words, "\n";
open ( my $phrases_fh, '<', 'phrases' ) or die $!;
while ( <$phrases_fh> ) {
if (m/$words/) {
s/$/ */;
}
print;
}
将输出重定向到所需位置。
最昂贵的部分是读取文件 - 它只执行一次。为每个搜索词的同一行重复调用正则表达式引擎也很昂贵,因为您将执行 N * M 次,其中 N 是单词数,M 是行数。
因此,我们编译一个正则表达式,并使用零宽度 \b
字边界标记匹配它(因此它不会匹配子字符串)。
注意 - 我们不引用 words
的内容 - 这可能是错误或功能,因为这意味着您可以将正则表达式添加到组合中。 (当我们编译正则表达式时,这可能会中断)。
如果你想确保它是'literal',那么:
my $words = join '"', map { quotemeta } map { s/\n//r } <$words_fh>;