如何在BASH中的文件中随机查找和替换一次匹配?

时间:2014-08-16 04:23:53

标签: bash perl sed

我正在使用sed来查找和替换文件中的项目:

sed -i "s/$pattern/$replacement/g" ./file.txt

这将替换文件中的所有外观。

有没有办法随机替换整个文件中的一个事件?

  • 匹配可能出现在文件中的任何行甚至多次,但只应更换一次。

3 个答案:

答案 0 :(得分:1)

使用perl,您可以使用rand功能:

perl -lane '
    while ($n = rand @F) { 
        if ($F[$n] eq "pattern") { 
            $F[$n] = "replacement"; 
            print "@F"; 
            last 
        } 
    }
' file

根据this帖子,shell中的变量可以在perl的%ENV哈希中找到。使用bash(以及其他一些shell),您需要采取额外的步骤“导出”shell变量,以便子进程可以看到它。

所以在你的情况下,你必须这样做:

pattern="mypattern"
replacement="myreplacement"
export pattern
export replacement
perl -lane '
    while ($n = rand @F) { 
        if ($F[$n] eq "$ENV{pattern}") { 
            $F[$n] = "$ENV{replacement}"; 
            print "@F"; 
            last 
        } 
    }
' file

当然,这将打印到STDOUT,因此如果您需要进行文件内更改,可以使用-i命令行选项或将输出重定向到另一个文件。

或者,如果这不是bash脚本的一部分,并且您希望在perl中执行此操作,则可以在命令行上传递模式和替换。

perl -lane '
BEGIN { ($patt, $repl) = splice @ARGV, 1 }
while ($n = rand @F) {
  if ($F[$n] eq $patt) {
    $F[$n] = $repl;
    print "@F";
    last
  }
}' file "pattern" "replacement"

这将每行随机更换一次。如果您希望每个文件执行一次,请发表评论,我将添加该版本。

答案 1 :(得分:1)

使用perl

#!/usr/bin/env perl

use List::Util qw(shuffle);
use strict;
use warnings;

my $search = shift @ARGV;
my $repl = shift @ARGV;
my @lines;
my @matches;

while (<>) {
    push(@lines, $_);
    push(@matches, $.) if /$search/;
}

my @shuffled = shuffle(@matches);
my $index = shift @shuffled;

if ($index) {
    $lines[$index - 1] =~ s/$search/$repl/;
}

print @lines;

用法:

perl script.pl string replacement file

答案 2 :(得分:1)

真正随机执行此操作的唯一方法是首先清点PATTERN的所有位置。否则就无法给予适当的加权。

以下通过脚本进行替换。它只替换当前特定行上模式的第一个引用:

use strict;
use warnings;
use autodie;

my $file = '...';
my $pattern = qr/.../;
my $replace = '...';

my @linenums;
local @ARGV = $file;
while (<>) {
    push @linenums, $_ if $_ =~ $pattern;
}

my $linenum = $linenums[rand @linenums];

local @ARGV = $file;
local $^I = '.bak';
while (<>) {
    s/$pattern/$replace/ if $. == $linenum;
    print;
}
# unlink "$file$^I"; # Optionally Delete backup

PATTERN的附录不止一次上线。

如果您想让一个模式不止一次出现在线上,可以使用以下增强功能:

use strict;
use warnings;
use autodie;

my $file = '...';
my $pattern = qr/.../;
my $replace = '...';

my @linenums;
local @ARGV = $file;
while (<>) {
    my $count = () = $_ =~ /$pattern/g;
    push @linenums, {line => $., num => $_} for 0 .. $count - 1;
}

die "No matches found" unless @linenums;

my $pick = $linenums[rand @linenums];

local @ARGV = $file;
local $^I = '.bak';
while (<>) {
    if ($. == $pick->{line}) {
        s/(?:$pattern.*?){$pick->{num}}\K$pattern/$replace/;
    }
    print;
}
# unlink "$file$^I"; # Optionally Delete backup